Instructions to use Pierfrancesco/adt-str with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Pierfrancesco/adt-str with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Pierfrancesco/adt-str", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download utils/drum_audio_render.py from Pierfrancesco/adt-str: direct link, hf CLI and curl.
- Browser
- Download file 5.67 kB
-
https://huggingface.co/Pierfrancesco/adt-str/resolve/main/utils/drum_audio_render.py
- Command line
-
hf download hf://Pierfrancesco/adt-str/utils/drum_audio_render.py
-
curl -L -o drum_audio_render.py https://huggingface.co/Pierfrancesco/adt-str/resolve/main/utils/drum_audio_render.py
5.67 kB
| """Drum preview: PrettyMIDI export + one-shot waveform rendering. | |
| Each note is rendered only if a matching WAV exists under ``one-shot-rendering/<pitch>/``. | |
| Notes without a sample are skipped (silent). MIDI export uses ``pretty_midi``. | |
| """ | |
| from __future__ import annotations | |
| from pathlib import Path | |
| from typing import Optional, Tuple, Union | |
| import glob | |
| import os | |
| import numpy as np | |
| import torch | |
| try: | |
| import pretty_midi | |
| except ImportError: # pragma: no cover | |
| pretty_midi = None # type: ignore | |
| def _velocity_to_midi(vel: float) -> int: | |
| v = float(vel) | |
| if v <= 1.0: | |
| v = int(round(v * 127)) | |
| else: | |
| v = int(round(v)) | |
| return int(np.clip(v, 1, 127)) | |
| def notes_to_pretty_midi( | |
| notes: Union[np.ndarray, torch.Tensor], | |
| name: str = "ADT drums", | |
| ) -> "pretty_midi.PrettyMIDI": | |
| if pretty_midi is None: | |
| raise RuntimeError("pretty_midi is required for MIDI export.") | |
| arr = ( | |
| notes.detach().cpu().numpy() | |
| if isinstance(notes, torch.Tensor) | |
| else np.asarray(notes, dtype=np.float64) | |
| ) | |
| pm = pretty_midi.PrettyMIDI() | |
| inst = pretty_midi.Instrument(program=0, is_drum=True, name=name) | |
| for row in arr: | |
| onset, offset, pitch, vel = ( | |
| float(row[0]), | |
| float(row[1]), | |
| int(row[2]), | |
| float(row[3]), | |
| ) | |
| if offset <= onset: | |
| offset = onset + 0.05 | |
| inst.notes.append( | |
| pretty_midi.Note( | |
| velocity=_velocity_to_midi(vel), | |
| pitch=int(np.clip(pitch, 0, 127)), | |
| start=onset, | |
| end=offset, | |
| ) | |
| ) | |
| pm.instruments.append(inst) | |
| return pm | |
| def save_drum_midi( | |
| notes: Union[np.ndarray, torch.Tensor], path: Union[str, Path] | |
| ) -> None: | |
| pm = notes_to_pretty_midi(notes) | |
| pm.write(str(path)) | |
| import wave | |
| _ONESHOT_CACHE = {} | |
| def get_oneshot_waveform( | |
| pitch: int, sample_rate: int, apply_mapping: bool = True | |
| ) -> Optional[np.ndarray]: | |
| global _ONESHOT_CACHE | |
| if not _ONESHOT_CACHE: | |
| try: | |
| from .mapping_utils import MappingUtils | |
| import torchaudio | |
| base_dir = Path(__file__).parent.parent / "one-shot-rendering" | |
| if base_dir.exists(): | |
| for d in os.listdir(base_dir): | |
| if not d.isdigit(): | |
| continue | |
| gm_custom = int(d) | |
| wavs = glob.glob(str(base_dir / d / "*.wav")) | |
| if wavs: | |
| wav_path = wavs[0] | |
| data, sr = torchaudio.load(wav_path) | |
| # Convert to mono if needed | |
| if data.shape[0] > 1: | |
| data = data.mean(dim=0, keepdim=True) | |
| # Resample if needed | |
| if sr != sample_rate: | |
| resampler = torchaudio.transforms.Resample( | |
| orig_freq=sr, new_freq=sample_rate | |
| ) | |
| data = resampler(data) | |
| _ONESHOT_CACHE[gm_custom] = ( | |
| data.squeeze().numpy().astype(np.float32) | |
| ) | |
| except Exception as e: | |
| print(f"Failed to load oneshot samples: {e}") | |
| pass | |
| custom_pitch = pitch | |
| if apply_mapping: | |
| try: | |
| from .mapping_utils import MappingUtils | |
| mapping = MappingUtils().GM_standard_midi_to_Gm_custom_Mapping | |
| custom_pitch = mapping.get(pitch, pitch) | |
| except Exception: | |
| pass | |
| return _ONESHOT_CACHE.get(custom_pitch, None) | |
| def synthesize_drums_procedural( | |
| notes: Union[np.ndarray, torch.Tensor], | |
| num_samples: int, | |
| sample_rate: int, | |
| apply_mapping: bool = True, | |
| ) -> np.ndarray: | |
| """Sum one-shot WAV hits into a mono float32 buffer. Skips notes with no sample.""" | |
| arr = ( | |
| notes.detach().cpu().numpy() | |
| if isinstance(notes, torch.Tensor) | |
| else np.asarray(notes, dtype=np.float64) | |
| ) | |
| if arr.size == 0: | |
| return np.zeros(num_samples, dtype=np.float32) | |
| buf = np.zeros(num_samples, dtype=np.float32) | |
| max_s = num_samples / float(sample_rate) | |
| for row in arr: | |
| onset, offset, pitch, vel = ( | |
| float(row[0]), | |
| float(row[1]), | |
| int(row[2]), | |
| float(row[3]), | |
| ) | |
| if onset >= max_s: | |
| continue | |
| i0 = int(onset * sample_rate) | |
| if i0 >= num_samples: | |
| continue | |
| hit = get_oneshot_waveform(pitch, sample_rate, apply_mapping=apply_mapping) | |
| if hit is None: | |
| continue | |
| n = min(len(hit), num_samples - i0) | |
| if n > 0: | |
| g = float(np.clip(vel if vel > 1.0 else vel * 127.0, 1.0, 127.0)) / 127.0 | |
| buf[i0 : i0 + n] += hit[:n] * g | |
| peak = np.abs(buf).max() | |
| if peak > 1e-6: | |
| buf *= min(1.0, 0.98 / peak) | |
| return buf | |
| def render_drum_preview( | |
| notes: Union[np.ndarray, torch.Tensor], | |
| num_samples: int, | |
| sample_rate: int, | |
| midi_path: Optional[Union[str, Path]] = None, | |
| apply_mapping: bool = False, | |
| ) -> Tuple[torch.Tensor, str]: | |
| """ | |
| Optionally write MIDI; audio is one-shot WAVs only (missing samples are silent). | |
| Returns | |
| ------- | |
| waveform : (num_samples,) float32 torch tensor | |
| mode : "oneshot" | |
| """ | |
| if midi_path is not None: | |
| save_drum_midi(notes, midi_path) | |
| wav = synthesize_drums_procedural( | |
| notes, num_samples, sample_rate, apply_mapping=apply_mapping | |
| ) | |
| return torch.from_numpy(wav), "oneshot" | |