| """build_electronic_reference.py — Seed the MERT reference DB with electronic |
| gear (drum machines + synths) so embedding-based ID works on electronic audio. |
| |
| Backs up the existing (acoustic) DB first, then adds several variants per gear |
| class (different tempos/seeds for drums, different pitches for synths) for |
| nearest-neighbor robustness. |
| |
| Usage: python3 tools/build_electronic_reference.py |
| """ |
| import logging |
| import os |
| import shutil |
| import sys |
| import tempfile |
|
|
| import numpy as np |
| import soundfile as sf |
|
|
| logging.basicConfig(level=logging.WARNING) |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
|
|
| from backend.mert_embeddings import MERTExtractor, get_reference_db, InstrumentReferenceDB |
| from tools.synth_gear import SR, KITS, SYNTHS, synth_riff |
|
|
| DRUM_SEEDS = [1, 2, 3] |
| |
| |
| SYNTH_ROOTS = [49, 65, 82, 98, 123, 147, 185, 233, 294] |
|
|
|
|
| def _embed_array(extractor, y): |
| with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tf: |
| path = tf.name |
| try: |
| sf.write(path, y, SR) |
| return extractor.extract(path) |
| finally: |
| os.unlink(path) |
|
|
|
|
| def main(): |
| db = get_reference_db() |
| |
| backup = InstrumentReferenceDB.DB_PATH.with_suffix(".backup_acoustic.json") |
| if not backup.exists(): |
| shutil.copy(InstrumentReferenceDB.DB_PATH, backup) |
| print(f"Backed up acoustic DB -> {backup.name}") |
|
|
| |
| existing_models = {e["model"] for e in db.entries} |
| already = existing_models & (set(KITS) | set(SYNTHS)) |
| if already: |
| print(f"Electronic entries already present ({len(already)} models). " |
| f"Delete them or restore the backup to rebuild. Aborting.") |
| return |
|
|
| start = len(db.entries) |
| extractor = MERTExtractor() |
| print(f"Seeding electronic gear (starting from {start} acoustic entries)…\n") |
|
|
| |
| for model, render in KITS.items(): |
| for seed in DRUM_SEEDS: |
| y = render(seed=seed) |
| emb = _embed_array(extractor, y) |
| db.add(emb, model=model, family="drum_machine", |
| source_file=f"synth:{model}:seed{seed}") |
| print(f" ✓ {model} ({len(DRUM_SEEDS)} variants)") |
|
|
| |
| for model, (family, fn) in SYNTHS.items(): |
| for root in SYNTH_ROOTS: |
| y = synth_riff(fn, root=root) |
| emb = _embed_array(extractor, y) |
| db.add(emb, model=model, family=family, |
| source_file=f"synth_riff:{model}:{root}Hz") |
| print(f" ✓ {model} ({len(SYNTH_ROOTS)} roots, family={family})") |
|
|
| added = len(db.entries) - start |
| print(f"\nDone. Added {added} electronic reference embeddings " |
| f"(DB now {len(db.entries)} total).") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|