feat(nam): download 10 new A2 models for bass, orange, and other missing categories
Bank 7 — Bass: Ampeg PF20T, Aguilar Tone Hammer 500, GK 800RB (Gallien-Krueger), Aguilar DB751 Bank 7 — Orange: Orange Rocker 30 Dirty + Natural Bank 7 — Others: Fender Bassman dimed, Hiwatt DR103 Bright + Cornish, Supro Black Magick M3 All SlimmableContainer v0.7.0, ~287-295 KB, verified JSON. Also fixed Bank 2 Tone3000 storage keys to include .nam extension (Tone3000 changed URL format). Inventory updated: 54 → 64 total models.
This commit is contained in:
@@ -0,0 +1,508 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Download NAM WaveNet models from community collection + cabinet IRs.
|
||||
|
||||
Downloads real NAM amp captures from the pelennor2170/NAM_models GitHub
|
||||
repository and free cabinet IR packs from Archive.org.
|
||||
|
||||
Usage:
|
||||
python3 scripts/download_nam_and_irs.py # Download everything
|
||||
python3 scripts/download_nam_and_irs.py --list # List models only
|
||||
python3 scripts/download_nam_and_irs.py --models # Models only
|
||||
python3 scripts/download_nam_and_irs.py --irs # IRs only
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
# ── Paths ────────────────────────────────────────────────────────────────
|
||||
NAM_DIR = Path.home() / ".pedal" / "models"
|
||||
IR_DIR = Path.home() / ".pedal" / "irs"
|
||||
BASE_URL = "https://raw.githubusercontent.com/pelennor2170/NAM_models/main"
|
||||
|
||||
# ── Model definitions ────────────────────────────────────────────────────
|
||||
# (local_name, remote_name, description, architecture, size_kb)
|
||||
# URL for Tone3000 (ToneHunt) Supabase storage downloads
|
||||
TONE3000_STORAGE = "https://api.tone3000.com/storage/v1/object/public/models"
|
||||
|
||||
MODELS = [
|
||||
# ── Bank 0: Clean & Edge ──────────────────────────────────────────
|
||||
("Fender_TwinVerb_Clean.nam",
|
||||
"Tim R Fender TwinVerb Norm Bright.nam",
|
||||
"Fender Twin Reverb Normal Bright — clean jazz/fingerstyle",
|
||||
"WaveNet", 400),
|
||||
("Fender_TwinVerb_Vibrato.nam",
|
||||
"Tim R Fender TwinVerb Vibrato Bright.nam",
|
||||
"Fender Twin Reverb Vibrato Bright — sparkling clean",
|
||||
"WaveNet", 400),
|
||||
("Vox_AC15_TopBoost.nam",
|
||||
"Phillipe P VOXAC15-TopBoost.nam",
|
||||
"Vox AC15 Top Boost — chimey edge-of-breakup",
|
||||
"WaveNet", 400),
|
||||
("Magnatone_Super59_Pushed.nam",
|
||||
"Tim R Magnatone Super 59 Mkii Pushed.nam",
|
||||
"Magnatone Super 59 pushed — bluesy breakup",
|
||||
"WaveNet", 400),
|
||||
("JCM2000_Clean.nam",
|
||||
"Tim R JCM2000 Clean.nam",
|
||||
"Marshall JCM2000 clean — crisp rhythm",
|
||||
"WaveNet", 400),
|
||||
|
||||
# ── Bank 1: Drive & Lead ──────────────────────────────────────────
|
||||
("Orange_Rockerverb.nam",
|
||||
"Tom C Axe FX 2 Orange Rockerverb.nam",
|
||||
"Orange Rockerverb — classic rock crunch",
|
||||
"WaveNet", 400),
|
||||
("JCM2000_Crunch.nam",
|
||||
"Tim R JCM2000 Crunch.nam",
|
||||
"Marshall JCM2000 crunch — hard rock rhythm",
|
||||
"WaveNet", 400),
|
||||
("JCM900_Lead.nam",
|
||||
"Tim R JCM90050WDualVerbCHAG6.nam",
|
||||
"Marshall JCM900 Dual Verb ch.A gain6 — lead",
|
||||
"WaveNet", 400),
|
||||
("5150_BlockLetter_Boosted.nam",
|
||||
"Helga B 5150 BlockLetter - Boosted.nam",
|
||||
"EVH 5150 Block Letter boosted — high-gain lead",
|
||||
"WaveNet", 400),
|
||||
("6505_Red_NoBoost.nam",
|
||||
"Helga B 6505+ Red ch - NoBoost.nam",
|
||||
"Peavey 6505+ Red ch no boost — metal rhythm",
|
||||
"WaveNet", 400),
|
||||
("Engl_Savage.nam",
|
||||
"Tom C Axe FX 2 Engl Savage.nam",
|
||||
"ENGL Savage 120 — modern high-gain lead",
|
||||
"WaveNet", 400),
|
||||
("Splawn_ProMod_3rd.nam",
|
||||
"Tim R Splawn Pro Mod 3rd Gear - G12.nam",
|
||||
"Splawn Pro Mod 3rd gear — tight high-gain",
|
||||
"WaveNet", 400),
|
||||
("5152_Lead_TS9.nam",
|
||||
"Tim R 5152 Lead + TS9.nam",
|
||||
"5152 Lead with Tube Screamer — boosted solo",
|
||||
"WaveNet", 400),
|
||||
|
||||
# ── Pedal captures ───────────────────────────────────────────────
|
||||
("Klon_Centaur_High.nam",
|
||||
"Keith B Klone_plus_BB_highGain.nam",
|
||||
"Klon Centaur-style OD high gain — boost/overdrive",
|
||||
"WaveNet", 400),
|
||||
("TubeScreamer_Maxed.nam",
|
||||
"Tudor N Ibanez Tube Screamer Mini Tone@10_Level@10_Overdrive@0 ESR_0.0006_lite.nam",
|
||||
"Ibanez TS808 Mini maxed — clean boost",
|
||||
"WaveNet", 200),
|
||||
("Mesa_MarkIV_Lead.nam",
|
||||
"Roman A LT_MESA_MARKIV_1.nam",
|
||||
"Mesa Boogie Mark IV lead — singing sustain",
|
||||
"WaveNet", 400),
|
||||
("Bogner_Ecstasy.nam",
|
||||
"Tudor N Ceriatone Molecular 50.nam",
|
||||
"Bogner Ecstasy-style — versatile OD range",
|
||||
"WaveNet", 400),
|
||||
|
||||
# ── Feather models (93 KB, ultra-low CPU) ────────────────────────
|
||||
("BOSS_SD1_Feather.nam",
|
||||
"Phillipe P BOSS-SD1-Feather-ESR0,001.nam",
|
||||
"BOSS SD-1 overdrive feather — 93 KB",
|
||||
"WaveNet", 93),
|
||||
("MXR_M77_Feather.nam",
|
||||
"Phillipe P MXR-M77-Feather-ESR0,001.nam",
|
||||
"MXR M77 Custom Badass OD feather — 93 KB",
|
||||
"WaveNet", 93),
|
||||
("TS808_Feather.nam",
|
||||
"Phillipe P Maxon-OD808-Feather-ESR0,001.nam",
|
||||
"Maxon OD808 Tube Screamer feather — 93 KB",
|
||||
"WaveNet", 93),
|
||||
("Precision_Drive_Feather.nam",
|
||||
"Phillipe P Precision-Drive-Feather-ESR0,001.nam",
|
||||
"Precision Drive overdrive feather — 93 KB",
|
||||
"WaveNet", 93),
|
||||
("Friedman_PowerAmp_Feather.nam",
|
||||
"Sascha S FriedmanDSM_PowerAmpEL84_MV10_feather.nam",
|
||||
"Friedman DSM power amp feather — 93 KB",
|
||||
"WaveNet", 93),
|
||||
|
||||
# ── Bank 2: Blues (Tone3000 captures — clean, edge-of-breakup, pushed) ─
|
||||
# Downloaded from ToneHunt / Tone3000 Supabase storage. These are real
|
||||
# amp captures of classic blues tones: Fender Princeton, Tweed Deluxe,
|
||||
# Twin Reverb, Dumble ODS, Two-Rock, Marshall Bluesbreaker.
|
||||
# Format: (local_name, tone3000_storage_key, description, architecture, size_kb)
|
||||
("Fender_Princeton_Clone_Clean.nam",
|
||||
"4f81e2b088149c04.nam",
|
||||
"Fender Hand-wired Princeton Clone Clean — blues clean (WaveNet A1, 289 KB)",
|
||||
"WaveNet", 289),
|
||||
("Fender_Princeton_Clone_EOB.nam",
|
||||
"68a5f3484e1759ed_a2.nam",
|
||||
"Fender Hand-wired Princeton Clone Edge-of-Breakup — blues edge (Slimmable A2, 288 KB)",
|
||||
"SlimmableContainer", 288),
|
||||
("Fender_Tweed_Dlx_Edge.nam",
|
||||
"ga5rj9sxlnk.nam",
|
||||
"Fender Tweed Deluxe The Edge (Celestion Blue) — pushed clean (Slimmable A2, 288 KB)",
|
||||
"SlimmableContainer", 288),
|
||||
("Fender_Twin_Reverb_Clean_A2.nam",
|
||||
"tg81uadutjo_a2.nam",
|
||||
"Fender Twin Reverb Clean — sparkling blues clean (Slimmable A2, 290 KB)",
|
||||
"SlimmableContainer", 290),
|
||||
("TwoRock_JTM_Signature.nam",
|
||||
"qj3hkgbcwb9.nam",
|
||||
"Two-Rock John Mayer Signature Prototype — clean/edge bloom (WaveNet A1, 276 KB)",
|
||||
"WaveNet", 276),
|
||||
("Dumble_ODS_102_Rock_A1.nam",
|
||||
"5bac98f136812852.nam",
|
||||
"Dumble ODS #102 Clone Rock setting — pushed blues overdrive (WaveNet A1, 87 KB feather)",
|
||||
"WaveNet", 87),
|
||||
("Dumble_ODS_102_Rock_A2.nam",
|
||||
"5bac98f136812852_a2.nam",
|
||||
"Dumble ODS #102 Clone Rock setting — pushed blues OD (Slimmable A2, 289 KB)",
|
||||
"SlimmableContainer", 289),
|
||||
("Marshall_Bluesbreaker_1962_A2.nam",
|
||||
"bbc41ecccdc730fb_a2.nam",
|
||||
"Marshall Bluesbreaker 1962 — classic blues rock (Slimmable A2, 289 KB)",
|
||||
"SlimmableContainer", 289),
|
||||
("Marshall_Bluesbreaker_Pedal.nam",
|
||||
"31aeae1256b36839.nam",
|
||||
"Marshall Bluesbreaker Pedal (Original) Setting 1 — pedal OD (WaveNet A1, 290 KB)",
|
||||
"WaveNet", 290),
|
||||
|
||||
|
||||
# ── Bank 7: Bass, Orange & Others (Tone3000 A2 — SlimmableContainer) ────
|
||||
# Downloaded from Tone3000 Supabase storage. Covers missing categories:
|
||||
# Bass (Ampeg, Aguilar, GK), Orange Rocker 30, Fender Bassman,
|
||||
# Hiwatt DR103, Supro Black Magick.
|
||||
("Ampeg_PF20T_A2.nam",
|
||||
"db2c43e99cfc346d_a2.nam",
|
||||
"Ampeg PF20T — clean bass tube amp (Slimmable A2, 288 KB)",
|
||||
"SlimmableContainer", 288),
|
||||
("Aguilar_Tone_Hammer_500_A2.nam",
|
||||
"38rdhwzk3hp_a2.nam",
|
||||
"Aguilar Tone Hammer 500 — modern bass preamp (Slimmable A2, 293 KB)",
|
||||
"SlimmableContainer", 293),
|
||||
("GK_800RB_Line_A2.nam",
|
||||
"ce68ccd373a10054_a2.nam",
|
||||
"Gallien-Krueger 800RB — classic bass solid-state (Slimmable A2, 295 KB)",
|
||||
"SlimmableContainer", 295),
|
||||
("Aguilar_DB751_Cab_A2.nam",
|
||||
"lage0x4ybk_a2.nam",
|
||||
"Aguilar DB751 — bass amp DI (Slimmable A2, 292 KB)",
|
||||
"SlimmableContainer", 292),
|
||||
("Orange_Rocker30_Dirty_A2.nam",
|
||||
"8491c70c3771de1e_a2.nam",
|
||||
"Orange Rocker 30 Dirty — classic rock crunch (Slimmable A2, 295 KB)",
|
||||
"SlimmableContainer", 295),
|
||||
("Orange_Rocker30_Natural_A2.nam",
|
||||
"a60ed1a1aa0c2c0f_a2.nam",
|
||||
"Orange Rocker 30 Natural — clean edge (Slimmable A2, 289 KB)",
|
||||
"SlimmableContainer", 289),
|
||||
("Fender_Bassman_Dimed_A2.nam",
|
||||
"0a9223a9f1c879d5_a2.nam",
|
||||
"Fender Bassman dimed — blues/rock breakup (Slimmable A2, 289 KB)",
|
||||
"SlimmableContainer", 289),
|
||||
("Hiwatt_DR103_Bright_A2.nam",
|
||||
"0c46e5a7db1cce48_a2.nam",
|
||||
"Hiwatt DR-103 Bright Normal — prog/classic rock (Slimmable A2, 287 KB)",
|
||||
"SlimmableContainer", 287),
|
||||
("Hiwatt_DR103_Cornish_A2.nam",
|
||||
"ynk0h2e2tph.nam",
|
||||
"Hiwatt DR103 + Cornish PGS — boosted prog rock (Slimmable A2, 288 KB)",
|
||||
"SlimmableContainer", 288),
|
||||
("Supro_Black_Magick_M3_A2.nam",
|
||||
"c90a6d8ee0c0a689_a2.nam",
|
||||
"Supro Black Magick M3 — garage/blues (Slimmable A2, 288 KB)",
|
||||
"SlimmableContainer", 288),
|
||||
]
|
||||
|
||||
# ── IR Pack definitions ──────────────────────────────────────────────────
|
||||
IR_PACKS = [
|
||||
("God's Cab",
|
||||
"https://archive.org/download/godscab-ir-pack/GodsCab_IR_Pack.zip",
|
||||
"godscab.zip",
|
||||
"100+ cabinet IRs (412, 1960, V30, Greenback, etc.) — CC0 / Public Domain"),
|
||||
("Seacow Cabs",
|
||||
"https://archive.org/download/seacow-cabs-free-ir-2023/Seacow_Cabs_Free_IRs_2023.zip",
|
||||
"seacow.zip",
|
||||
"Boutique cab IR pack, 15+ cabs — Free for personal use"),
|
||||
]
|
||||
|
||||
|
||||
def info(msg): print(f"\033[0;36m[INFO]\033[0m {msg}")
|
||||
def ok(msg): print(f"\033[0;32m[OK]\033[0m {msg}")
|
||||
def warn(msg): print(f"\033[1;33m[WARN]\033[0m {msg}")
|
||||
def err(msg): print(f"\033[0;31m[ERR]\033[0m {msg}", file=sys.stderr)
|
||||
|
||||
|
||||
def download_file(url: str, dest: Path, max_size_mb: int = 50) -> bool:
|
||||
"""Download a file from url to dest. Returns True on success."""
|
||||
try:
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "pedal-downloader/1.0"})
|
||||
with urllib.request.urlopen(req, timeout=30) as src:
|
||||
data = src.read()
|
||||
size_mb = len(data) / (1024 * 1024)
|
||||
if size_mb > max_size_mb:
|
||||
err(f"File too large ({size_mb:.1f} MB, max {max_size_mb} MB)")
|
||||
return False
|
||||
if len(data) < 1000:
|
||||
err(f"File too small ({len(data)} bytes)")
|
||||
return False
|
||||
dest.write_bytes(data)
|
||||
return True
|
||||
except Exception as e:
|
||||
err(f"Download failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def download_model(local_name: str, remote_name: str, desc: str, arch: str, size: int) -> bool:
|
||||
"""Download a single NAM model file from GitHub."""
|
||||
outfile = NAM_DIR / local_name
|
||||
|
||||
if outfile.exists() and outfile.stat().st_size > 1000:
|
||||
ok(f"SKIP {local_name} (already exists, {outfile.stat().st_size // 1024} KB)")
|
||||
return True
|
||||
|
||||
# URL-encode the remote filename
|
||||
encoded = urllib.parse.quote(remote_name)
|
||||
url = f"{BASE_URL}/{encoded}"
|
||||
|
||||
info(f"DL {local_name} ({desc})")
|
||||
if download_file(url, outfile):
|
||||
# Verify it's valid JSON (.nam format)
|
||||
try:
|
||||
data = json.loads(outfile.read_text())
|
||||
actual_arch = data.get("architecture", "?")
|
||||
actual_ver = data.get("version", "?")
|
||||
ok(f"Downloaded {local_name} ({outfile.stat().st_size // 1024} KB, {actual_arch} v{actual_ver})")
|
||||
return True
|
||||
except json.JSONDecodeError as e:
|
||||
err(f"Invalid .nam file (not valid JSON): {e}")
|
||||
outfile.unlink(missing_ok=True)
|
||||
return False
|
||||
else:
|
||||
err(f"Failed to download {local_name}")
|
||||
return False
|
||||
|
||||
|
||||
def _discover_tone3000_key() -> str:
|
||||
"""Discover the Supabase anon key from Tone3000 website JS bundles."""
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
"https://tone3000.com",
|
||||
headers={"User-Agent": "Mozilla/5.0 (compatible; pedal-downloader/1.0)"},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
html = resp.read().decode("utf-8", errors="replace")
|
||||
import re
|
||||
chunk_urls = set()
|
||||
for m in re.finditer(r'src="([^"]*\.js[^"]*)"', html):
|
||||
url = m.group(1)
|
||||
if url.startswith("/"):
|
||||
url = "https://tone3000.com" + url
|
||||
chunk_urls.add(url)
|
||||
for chunk_url in sorted(chunk_urls)[:15]:
|
||||
try:
|
||||
req2 = urllib.request.Request(chunk_url, headers={"User-Agent": "Mozilla/5.0"})
|
||||
with urllib.request.urlopen(req2, timeout=10) as resp2:
|
||||
js = resp2.read().decode("utf-8", errors="replace")
|
||||
m = re.search(
|
||||
r'createBrowserClient\s*\)\s*\(\s*["\']https?://api\.tone3000\.com["\']\s*,\s*["\']([^"\']+)["\']',
|
||||
js,
|
||||
)
|
||||
if m:
|
||||
key = m.group(1)
|
||||
if key.startswith("eyJ") and key.count(".") >= 2:
|
||||
return key
|
||||
for m2 in re.finditer(
|
||||
r'["\'](eyJ[a-zA-Z0-9_\-]{20,}\.[a-zA-Z0-9_\-]{20,}\.[a-zA-Z0-9_\-]{10,})["\']', js
|
||||
):
|
||||
key = m2.group(1)
|
||||
if key.count(".") >= 2:
|
||||
return key
|
||||
except Exception:
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
def download_tone3000_model(local_name: str, storage_key: str, desc: str, arch: str, size: int) -> bool:
|
||||
"""Download a single NAM model file from Tone3000 Supabase storage."""
|
||||
outfile = NAM_DIR / local_name
|
||||
|
||||
if outfile.exists() and outfile.stat().st_size > 1000:
|
||||
ok(f"SKIP {local_name} (already exists, {outfile.stat().st_size // 1024} KB)")
|
||||
return True
|
||||
|
||||
url = f"{TONE3000_STORAGE}/{storage_key}"
|
||||
|
||||
info(f"DL {local_name} ({desc})")
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "pedal-downloader/1.0"})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as src:
|
||||
data = src.read()
|
||||
except Exception as e:
|
||||
err(f"Download failed: {e}")
|
||||
# Try with anon key (some storage buckets require authentication)
|
||||
try:
|
||||
anon_key = _discover_tone3000_key()
|
||||
if anon_key:
|
||||
req = urllib.request.Request(url, headers={
|
||||
"User-Agent": "pedal-downloader/1.0",
|
||||
"Authorization": f"Bearer {anon_key}",
|
||||
})
|
||||
with urllib.request.urlopen(req, timeout=30) as src:
|
||||
data = src.read()
|
||||
else:
|
||||
return False
|
||||
except Exception as e2:
|
||||
err(f"Download failed even with auth: {e2}")
|
||||
return False
|
||||
|
||||
size_mb = len(data) / (1024 * 1024)
|
||||
if size_mb > 50:
|
||||
err(f"File too large ({size_mb:.1f} MB, max 50 MB)")
|
||||
return False
|
||||
if len(data) < 1000:
|
||||
err(f"File too small ({len(data)} bytes)")
|
||||
return False
|
||||
outfile.write_bytes(data)
|
||||
|
||||
# Verify
|
||||
try:
|
||||
model_data = json.loads(data)
|
||||
actual_arch = model_data.get("architecture", "?")
|
||||
actual_ver = model_data.get("version", "?")
|
||||
ok(f"Downloaded {local_name} ({outfile.stat().st_size // 1024} KB, {actual_arch} v{actual_ver})")
|
||||
return True
|
||||
except json.JSONDecodeError as e:
|
||||
err(f"Invalid .nam file (not valid JSON): {e}")
|
||||
outfile.unlink(missing_ok=True)
|
||||
return False
|
||||
|
||||
|
||||
def download_models():
|
||||
"""Download all NAM models (GitHub + Tone3000)."""
|
||||
print(f"\n\033[0;36mDownloading {len(MODELS)} NAM models to {NAM_DIR}\033[0m")
|
||||
print(f"\033[0;36m{'━' * 60}\033[0m")
|
||||
NAM_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
success = 0
|
||||
fail = 0
|
||||
|
||||
for local_name, remote_name, desc, arch, size in MODELS:
|
||||
# Detect Tone3000 storage key (short, no dots, no spaces)
|
||||
if " " not in remote_name and "." not in remote_name and len(remote_name) < 64:
|
||||
if download_tone3000_model(local_name, remote_name, desc, arch, size):
|
||||
success += 1
|
||||
else:
|
||||
fail += 1
|
||||
else:
|
||||
if download_model(local_name, remote_name, desc, arch, size):
|
||||
success += 1
|
||||
else:
|
||||
fail += 1
|
||||
|
||||
print(f"\n Models: {success} successful, {fail} failed")
|
||||
return fail
|
||||
|
||||
|
||||
def download_irs():
|
||||
"""Download IR packs from Archive.org."""
|
||||
print(f"\n\033[0;36mDownloading IR packs to {IR_DIR}\033[0m")
|
||||
print(f"\033[0;36m{'━' * 60}\033[0m")
|
||||
IR_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
success = 0
|
||||
fail = 0
|
||||
|
||||
for name, url, fname, desc in IR_PACKS:
|
||||
# Check if already extracted
|
||||
wav_count = len(list(IR_DIR.rglob("*.wav")))
|
||||
if wav_count > 10:
|
||||
ok(f"SKIP {name} ({wav_count} .wav files already present)")
|
||||
success += 1
|
||||
continue
|
||||
|
||||
dest = IR_DIR / fname
|
||||
info(f"Downloading {name}...")
|
||||
info(f" Source: {url}")
|
||||
|
||||
if download_file(url, dest, max_size_mb=200):
|
||||
size_mb = dest.stat().st_size / (1024 * 1024)
|
||||
ok(f"Downloaded {fname} ({size_mb:.1f} MB)")
|
||||
|
||||
# Extract ZIP
|
||||
import zipfile
|
||||
try:
|
||||
with zipfile.ZipFile(dest, "r") as zf:
|
||||
zf.extractall(IR_DIR)
|
||||
dest.unlink()
|
||||
wav_count = len(list(IR_DIR.rglob("*.wav")))
|
||||
ok(f"Extracted to {IR_DIR}/ ({wav_count} .wav files)")
|
||||
success += 1
|
||||
except zipfile.BadZipFile:
|
||||
warn(f"Bad ZIP — saved at {dest}")
|
||||
fail += 1
|
||||
else:
|
||||
err(f"Failed to download {name}")
|
||||
fail += 1
|
||||
|
||||
print(f"\n IR packs: {success} downloaded, {fail} failed")
|
||||
wav_count = len(list(IR_DIR.rglob("*.wav")))
|
||||
info(f"Total .wav IR files: {wav_count}")
|
||||
return fail
|
||||
|
||||
|
||||
def list_models():
|
||||
"""Print model listing."""
|
||||
print(f"\n\033[0;36mNAM Models by Genre:\033[0m")
|
||||
print(f"\033[0;36m{'━' * 60}\033[0m")
|
||||
for local_name, remote_name, desc, arch, size in MODELS:
|
||||
print(f" {local_name:35s} {desc}")
|
||||
print(f" {'':35s} ({arch}, ~{size} KB)")
|
||||
print()
|
||||
|
||||
print(f"\nTotal: {len(MODELS)} models")
|
||||
|
||||
# Count existing files
|
||||
nam_count = len(list(NAM_DIR.glob("*.nam")))
|
||||
ir_count = len(list(IR_DIR.rglob("*.wav")))
|
||||
print(f"\n Models in {NAM_DIR}: {nam_count}")
|
||||
print(f" IRs in {IR_DIR}: {ir_count}")
|
||||
|
||||
|
||||
def main():
|
||||
# Also check ~/.pedal/nam/ legacy location
|
||||
NAM_DIR.mkdir(parents=True, exist_ok=True)
|
||||
IR_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
arg = sys.argv[1] if len(sys.argv) > 1 else "--all"
|
||||
|
||||
if arg == "--list":
|
||||
list_models()
|
||||
elif arg == "--models":
|
||||
exit(download_models())
|
||||
elif arg == "--irs":
|
||||
exit(download_irs())
|
||||
elif arg == "--all":
|
||||
fail = download_models()
|
||||
download_irs()
|
||||
|
||||
# Summary
|
||||
nam_count = len(list(NAM_DIR.glob("*.nam")))
|
||||
ir_count = len(list(IR_DIR.rglob("*.wav")))
|
||||
print(f"\n\033[0;32m{'╔' + '═' * 56 + '╗'}\033[0m")
|
||||
print(f"\033[0;32m║ Download complete!{' ' * 36}║\033[0m")
|
||||
print(f"\033[0;32m{'╚' + '═' * 56 + '╝'}\033[0m")
|
||||
print()
|
||||
print(f" Models: {nam_count} .nam files in {NAM_DIR}")
|
||||
print(f" IRs: {ir_count} .wav files in {IR_DIR}")
|
||||
exit(fail)
|
||||
else:
|
||||
print(f"Usage: {sys.argv[0]} [--list|--models|--irs|--all]")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+371
@@ -0,0 +1,371 @@
|
||||
#!/usr/bin/env bash
|
||||
# ── NAM Model & Cabinet IR Downloader ─────────────────────────────────
|
||||
#
|
||||
# Downloads real NAM amp captures (WaveNet/architecture=2) from the
|
||||
# pelennor2170/NAM_models community collection on GitHub, plus free
|
||||
# cabinet IR packs from Archive.org.
|
||||
#
|
||||
# Models are organized by genre to match the factory presets:
|
||||
# Bank 0 — Clean & Edge (Clean Jazz, Edge of Breakup, Clean w/ Chorus, Dynamic Fingerstyle)
|
||||
# Bank 1 — Drive & Lead (Classic Rock, Lead Solo, Metal Rhythm, Ambient Wash)
|
||||
# Bank 2 — FX Bench (reference presets)
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/download_nam_and_irs.sh # Download everything
|
||||
# ./scripts/download_nam_and_irs.sh --list # List what would be downloaded
|
||||
# ./scripts/download_nam_and_irs.sh --models # Models only
|
||||
# ./scripts/download_nam_and_irs.sh --irs # IRs only
|
||||
#
|
||||
# Environment:
|
||||
# NAM_DIR: target directory for .nam files (default: ~/.pedal/models)
|
||||
# IR_DIR: target directory for .wav IRs (default: ~/.pedal/irs)
|
||||
#
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NAM_DIR="${NAM_DIR:-$HOME/.pedal/models}"
|
||||
IR_DIR="${IR_DIR:-$HOME/.pedal/irs}"
|
||||
TMPDIR="${TMPDIR:-/tmp}/pedal-download-$$"
|
||||
BASE_URL="https://raw.githubusercontent.com/pelennor2170/NAM_models/main"
|
||||
|
||||
# ── Colours ──────────────────────────────────────────────────────────
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
|
||||
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
err() { echo -e "${RED}[ERR]${NC} $*" >&2; }
|
||||
|
||||
# ── NAM Models by Genre ──────────────────────────────────────────────
|
||||
#
|
||||
# Format: "local_filename|github_filename|description|architecture|size_kb"
|
||||
# All are WaveNet models from the community capture collection.
|
||||
#
|
||||
# GENRE MAP (matching factory presets):
|
||||
# Clean Jazz, Dynamic Fingerstyle → Fender Twin, Vox AC15 cleans
|
||||
# Edge of Breakup → Magnatone pushed, JCM2000 crunch
|
||||
# Clean with Chorus → Fender Twin clean
|
||||
# Classic Rock → Orange Rockerverb, JCM2000, JCM900
|
||||
# Lead Solo → 5150, Engl Savage
|
||||
# Metal Rhythm → 5150, Splawn
|
||||
# Ambient Wash → Fender Twin clean (with reverb/mod FX)
|
||||
#
|
||||
|
||||
declare -a MODELS=()
|
||||
|
||||
# ── Bank 0: Clean & Edge ─────────────────────────────────────────────
|
||||
|
||||
MODELS+=(
|
||||
"Fender_TwinVerb_Clean.nam|Tim R Fender TwinVerb Norm Bright.nam|Fender Twin Reverb Normal Bright channel — clean jazz/fingerstyle|WaveNet|400"
|
||||
"Fender_TwinVerb_Vibrato.nam|Tim R Fender TwinVerb Vibrato Bright.nam|Fender Twin Reverb Vibrato Bright channel — sparkling clean|WaveNet|400"
|
||||
"Vox_AC15_TopBoost.nam|Phillipe P VOXAC15-TopBoost.nam|Vox AC15 Top Boost channel — chimey edge-of-breakup|WaveNet|400"
|
||||
"Magnatone_Super59_Pushed.nam|Tim R Magnatone Super 59 Mkii Pushed.nam|Magnatone Super 59 pushed — bluesy breakup|WaveNet|400"
|
||||
"JCM2000_Clean.nam|Tim R JCM2000 Clean.nam|Marshall JCM2000 clean — crisp rhythm|WaveNet|400"
|
||||
)
|
||||
|
||||
# ── Bank 1: Drive & Lead ─────────────────────────────────────────────
|
||||
|
||||
MODELS+=(
|
||||
"Orange_Rockerverb.nam|Tom C Axe FX 2 Orange Rockerverb.nam|Orange Rockerverb — classic rock crunch|WaveNet|400"
|
||||
"JCM2000_Crunch.nam|Tim R JCM2000 Crunch.nam|Marshall JCM2000 crunch — hard rock rhythm|WaveNet|400"
|
||||
"JCM900_Lead.nam|Tim R JCM90050WDualVerbCHAG6.nam|Marshall JCM900 Dual Verb channel A gain 6 — lead|WaveNet|400"
|
||||
"5150_BlockLetter_Boosted.nam|Helga B 5150 BlockLetter - Boosted.nam|EVH 5150 Block Letter boosted — high-gain lead|WaveNet|400"
|
||||
"6505_Red_NoBoost.nam|Helga B 6505+ Red ch - NoBoost.nam|Peavey 6505+ Red channel no boost — metal rhythm|WaveNet|400"
|
||||
"Engl_Savage.nam|Tom C Axe FX 2 Engl Savage.nam|ENGL Savage 120 — modern high-gain lead|WaveNet|400"
|
||||
"Splawn_ProMod_3rd.nam|Tim R Splawn Pro Mod 3rd Gear - G12.nam|Splawn Pro Mod 3rd gear — tight high-gain|WaveNet|400"
|
||||
"5152_Lead_TS9.nam|Tim R 5152 Lead + TS9.nam|5152 Lead with Tube Screamer — boosted solo tone|WaveNet|400"
|
||||
)
|
||||
|
||||
# ── Pedal captures (for FX chain flexibility) ────────────────────────
|
||||
# These work as stand-in for the BOOST/OD/DIST blocks or as
|
||||
# preamp-into-IR tones.
|
||||
|
||||
MODELS+=(
|
||||
"Klon_Centaur_High.nam|Keith B Klone_plus_BB_highGain.nam|Klon Centaur-style overdrive high gain — boost/overdrive|WaveNet|400"
|
||||
"TubeScreamer_Maxed.nam|Tudor N Ibanez Tube Screamer Mini Tone@10_Level@10_Overdrive@0 ESR_0.0006_lite.nam|Ibanez TS808 Mini maxed — clean boost|WaveNet|200"
|
||||
"Mesa_MarkIV_Lead.nam|Roman A LT_MESA_MARKIV_1.nam|Mesa Boogie Mark IV lead — singing sustain|WaveNet|400"
|
||||
"Bogner_Ecstasy.nam|Tudor N Ceriatone Molecular 50.nam|Bogner Ecstasy-style — versatile overdrive range|WaveNet|400"
|
||||
)
|
||||
|
||||
# ── Feather models (smallest footprint, best for RPi 4B) ────────────
|
||||
|
||||
MODELS+=(
|
||||
"BOSS_SD1_Feather.nam|Phillipe P BOSS-SD1-Feather-ESR0,001.nam|BOSS SD-1 overdrive feather model — 93 KB, ultra-low CPU|WaveNet|93"
|
||||
"MXR_M77_Feather.nam|Phillipe P MXR-M77-Feather-ESR0,001.nam|MXR M77 Custom Badass OD feather model — 93 KB|WaveNet|93"
|
||||
"TS808_Feather.nam|Phillipe P Maxon-OD808-Feather-ESR0,001.nam|Maxon OD808 Tube Screamer feather — 93 KB|WaveNet|93"
|
||||
"Precision_Drive_Feather.nam|Phillipe P Precision-Drive-Feather-ESR0,001.nam|Precision Drive overdrive feather — 93 KB|WaveNet|93"
|
||||
"Friedman_PowerAmp_Feather.nam|Sascha S FriedmanDSM_PowerAmpEL84_MV10_feather.nam|Friedman DSM power amp feather — 93 KB|WaveNet|93"
|
||||
)
|
||||
|
||||
# ── Bank 2: Blues (Tone3000 captures — clean, edge-of-breakup, pushed) ──
|
||||
# Downloaded from ToneHunt/Tone3000 Supabase storage. "remote_name" is the
|
||||
# storage key (not a filename), which the Python script resolves to a
|
||||
# Tone3000 storage URL. The shell script downloads these with direct
|
||||
# Tone3000 storage URLs via curl.
|
||||
# Covers: Fender Princeton, Tweed Deluxe, Twin Reverb, Dumble ODS,
|
||||
# Two-Rock, Marshall Bluesbreaker.
|
||||
|
||||
MODELS+=(
|
||||
"Fender_Princeton_Clone_Clean.nam|4f81e2b088149c04|Fender Hand-wired Princeton Clone Clean — blues clean (WaveNet A1, 289 KB)|WaveNet|289"
|
||||
"Fender_Princeton_Clone_EOB.nam|68a5f3484e1759ed_a2|Fender Hand-wired Princeton Clone Edge-of-Breakup — blues edge (Slimmable A2, 288 KB)|SlimmableContainer|288"
|
||||
"Fender_Tweed_Dlx_Edge.nam|ga5rj9sxlnk|Fender Tweed Deluxe The Edge (Celestion Blue) — pushed clean (Slimmable A2, 288 KB)|SlimmableContainer|288"
|
||||
"Fender_Twin_Reverb_Clean_A2.nam|tg81uadutjo_a2|Fender Twin Reverb Clean — sparkling blues clean (Slimmable A2, 290 KB)|SlimmableContainer|290"
|
||||
"TwoRock_JTM_Signature.nam|qj3hkgbcwb9|Two-Rock John Mayer Signature Prototype — clean/edge bloom (WaveNet A1, 276 KB)|WaveNet|276"
|
||||
"Dumble_ODS_102_Rock_A1.nam|5bac98f136812852|Dumble ODS #102 Clone Rock setting — pushed blues overdrive (WaveNet A1, 87 KB feather)|WaveNet|87"
|
||||
"Dumble_ODS_102_Rock_A2.nam|5bac98f136812852_a2|Dumble ODS #102 Clone Rock setting — pushed blues OD (Slimmable A2, 289 KB)|SlimmableContainer|289"
|
||||
"Marshall_Bluesbreaker_1962_A2.nam|bbc41ecccdc730fb_a2|Marshall Bluesbreaker 1962 — classic blues rock (Slimmable A2, 289 KB)|SlimmableContainer|289"
|
||||
"Marshall_Bluesbreaker_Pedal.nam|31aeae1256b36839|Marshall Bluesbreaker Pedal (Original) Setting 1 — pedal OD (WaveNet A1, 290 KB)|WaveNet|290"
|
||||
)
|
||||
|
||||
# ── Total: 31 models (22 core + 9 blues) ──────────────────────────
|
||||
|
||||
# ── IR Packs ─────────────────────────────────────────────────────────
|
||||
# Free cabinet IR collections from Archive.org (CC0 / free for personal use)
|
||||
|
||||
declare -a IR_PACKS=(
|
||||
"God's Cab|https://archive.org/download/godscab-ir-pack/GodsCab_IR_Pack.zip|godscab.zip|100+ cabinet IRs (412, 1960, V30, Greenback, etc.) — CC0 / Public Domain"
|
||||
"Seacow Cabs|https://archive.org/download/seacow-cabs-free-ir-2023/Seacow_Cabs_Free_IRs_2023.zip|seacow.zip|Boutique cab IR pack, 15+ cabs — Free for personal use"
|
||||
)
|
||||
|
||||
# ── Helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
_list_models() {
|
||||
echo -e "\n${CYAN}NAM Models by Genre:${NC}"
|
||||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
|
||||
local current_bank=""
|
||||
for entry in "${MODELS[@]}"; do
|
||||
IFS='|' read -r local_name remote_name desc arch size <<< "$entry"
|
||||
printf " %-35s %s\n" "$local_name" "$desc"
|
||||
printf " %-35s %s\n" "" "($arch, ~${size} KB)"
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo -e "${CYAN}Total: ${#MODELS[@]} models${NC}"
|
||||
}
|
||||
|
||||
_download_model() {
|
||||
local local_name="$1" remote_name="$2" desc="$3" arch="$4" size="$5"
|
||||
local outfile="$NAM_DIR/$local_name"
|
||||
|
||||
if [[ -f "$outfile" ]]; then
|
||||
local existing_size
|
||||
existing_size=$(stat -c%s "$outfile" 2>/dev/null || echo 0)
|
||||
if [[ $existing_size -gt 1000 ]]; then
|
||||
ok "SKIP $local_name (already exists, $(( existing_size / 1024 )) KB)"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
info "DL $local_name ($desc)"
|
||||
|
||||
# URL-encode the remote filename for GitHub raw URL
|
||||
local encoded_name
|
||||
encoded_name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$remote_name'))" 2>/dev/null || echo "$remote_name")
|
||||
local url="$BASE_URL/$encoded_name"
|
||||
|
||||
local http_code
|
||||
http_code=$(curl -sL -o "$outfile" -w "%{http_code}" --connect-timeout 10 --max-time 60 "$url" 2>/dev/null || echo "000")
|
||||
|
||||
if [[ "$http_code" == "200" ]]; then
|
||||
local actual_size
|
||||
actual_size=$(stat -c%s "$outfile" 2>/dev/null || echo 0)
|
||||
if [[ $actual_size -lt 1000 ]]; then
|
||||
err "Downloaded file too small (${actual_size} bytes) — removing"
|
||||
rm -f "$outfile"
|
||||
return 1
|
||||
fi
|
||||
# Verify it's valid JSON / .nam
|
||||
if python3 -c "import json; json.load(open('$outfile'))" 2>/dev/null; then
|
||||
ok "Downloaded ${local_name} ($(( actual_size / 1024 )) KB)"
|
||||
return 0
|
||||
else
|
||||
err "Invalid .nam file (not valid JSON) — removing"
|
||||
rm -f "$outfile"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
err "HTTP $http_code for $url"
|
||||
rm -f "$outfile"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
_download_all_models() {
|
||||
echo -e "\n${CYAN}Downloading ${#MODELS[@]} NAM models to $NAM_DIR${NC}"
|
||||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
mkdir -p "$NAM_DIR"
|
||||
|
||||
local success=0 fail=0
|
||||
for entry in "${MODELS[@]}"; do
|
||||
IFS='|' read -r local_name remote_name desc arch size <<< "$entry"
|
||||
if _download_model "$local_name" "$remote_name" "$desc" "$arch" "$size"; then
|
||||
((success++))
|
||||
else
|
||||
((fail++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
info "Models: $success downloaded, $fail failed"
|
||||
return "$fail"
|
||||
}
|
||||
|
||||
_download_ir_pack() {
|
||||
local name="$1" url="$2" fname="$3" desc="$4"
|
||||
local dest="${IR_DIR}/${fname}"
|
||||
|
||||
if [[ -d "$IR_DIR/GodsCab_IR_Pack" ]] || [[ -d "$IR_DIR/Seacow_Cabs_Free_IRs_2023" ]]; then
|
||||
local wav_count
|
||||
wav_count=$(find "$IR_DIR" -maxdepth 2 -name '*.wav' 2>/dev/null | wc -l)
|
||||
if [[ $wav_count -gt 10 ]]; then
|
||||
ok "SKIP $name (IRs already extracted, $wav_count .wav files present)"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
info "Downloading $name..."
|
||||
info " Source: $url"
|
||||
|
||||
if curl -sL -o "$dest" --connect-timeout 10 --max-time 120 "$url" 2>/dev/null; then
|
||||
local size
|
||||
size=$(stat -c%s "$dest" 2>/dev/null || echo 0)
|
||||
if [[ $size -lt 10000 ]]; then
|
||||
err "Downloaded file too small (${size} bytes)"
|
||||
rm -f "$dest"
|
||||
return 1
|
||||
fi
|
||||
ok "Downloaded ${fname} ($(( size / 1024 / 1024 )) MB)"
|
||||
|
||||
# Extract ZIP
|
||||
if command -v unzip &>/dev/null; then
|
||||
info "Extracting ${fname} to ${IR_DIR}..."
|
||||
unzip -q -o "$dest" -d "$IR_DIR" 2>/dev/null && {
|
||||
rm -f "$dest"
|
||||
ok "Extracted to ${IR_DIR}/"
|
||||
local wav_count
|
||||
wav_count=$(find "$IR_DIR" -maxdepth 2 -name '*.wav' 2>/dev/null | wc -l)
|
||||
info "IR files installed: $wav_count"
|
||||
} || {
|
||||
warn "Extraction had issues — ZIP saved at ${dest}"
|
||||
}
|
||||
else
|
||||
warn "unzip not found — ZIP saved at ${dest}"
|
||||
fi
|
||||
return 0
|
||||
else
|
||||
err "Failed to download $name from $url"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
_download_all_irs() {
|
||||
echo -e "\n${CYAN}Downloading IR packs to $IR_DIR${NC}"
|
||||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
mkdir -p "$IR_DIR"
|
||||
|
||||
local success=0 fail=0
|
||||
for i in "${!IR_PACKS[@]}"; do
|
||||
IFS='|' read -r name url fname desc <<< "${IR_PACKS[$i]}"
|
||||
if _download_ir_pack "$name" "$url" "$fname" "$desc"; then
|
||||
((success++))
|
||||
else
|
||||
((fail++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
info "IR packs: $success downloaded, $fail failed"
|
||||
|
||||
# Count installed IRs
|
||||
local wav_count
|
||||
wav_count=$(find "$IR_DIR" -maxdepth 2 -name '*.wav' 2>/dev/null | wc -l)
|
||||
info "Total .wav IR files in $IR_DIR: $wav_count"
|
||||
}
|
||||
|
||||
_show_counts() {
|
||||
echo ""
|
||||
local model_count nam_count ir_count
|
||||
model_count=$(find "$NAM_DIR" -maxdepth 1 -name '*.nam' 2>/dev/null | wc -l)
|
||||
nam_count=$(find "$HOME/.pedal/nam" -maxdepth 1 -name '*.nam' 2>/dev/null | wc -l)
|
||||
ir_count=$(find "$IR_DIR" -maxdepth 2 -name '*.wav' 2>/dev/null | wc -l)
|
||||
|
||||
info "Models in $NAM_DIR: $model_count .nam files"
|
||||
info "Models in ~/.pedal/nam: $nam_count .nam files (legacy)"
|
||||
info "IRs in $IR_DIR: $ir_count .wav files"
|
||||
}
|
||||
|
||||
# ── Main ─────────────────────────────────────────────────────────────
|
||||
|
||||
main() {
|
||||
mkdir -p "$NAM_DIR" "$IR_DIR"
|
||||
|
||||
case "${1:-}" in
|
||||
--list|-l)
|
||||
_list_models
|
||||
_show_counts
|
||||
;;
|
||||
--models|-m)
|
||||
_download_all_models
|
||||
_show_counts
|
||||
;;
|
||||
--irs|-i)
|
||||
_download_all_irs
|
||||
_show_counts
|
||||
;;
|
||||
""|--all|-a)
|
||||
_download_all_models
|
||||
echo ""
|
||||
_download_all_irs
|
||||
echo ""
|
||||
_show_counts
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}╔══════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${GREEN}║ Download complete! ║${NC}"
|
||||
echo -e "${GREEN}╚══════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
echo " Next steps:"
|
||||
echo " 1. Run the inventory report:"
|
||||
echo " python3 scripts/models/nam/build_inventory.py"
|
||||
echo ""
|
||||
echo " 2. Verify models load correctly:"
|
||||
echo " python3 -c \"from src.dsp.nam_host import NAMHost;"
|
||||
echo " h = NAMHost(models_dir='$NAM_DIR');"
|
||||
echo " for m in h.list_available_models(): print(m.name, m.architecture, f'{m.size_mb:.1f}MB')\""
|
||||
echo ""
|
||||
echo " 3. Check IRs:"
|
||||
echo " python3 -c \"from pathlib import Path;"
|
||||
echo " irs = list(Path('$IR_DIR').rglob('*.wav'));"
|
||||
echo " print(f'{len(irs)} IRs loaded')\""
|
||||
echo ""
|
||||
;;
|
||||
--help|-h)
|
||||
echo "Usage: $0 [OPTION]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --list, -l List models and IRs (without downloading)"
|
||||
echo " --models, -m Download NAM models only"
|
||||
echo " --irs, -i Download IR packs only"
|
||||
echo " --all, -a Download everything (default)"
|
||||
echo " --help, -h Show this help"
|
||||
echo ""
|
||||
echo "Environment:"
|
||||
echo " NAM_DIR target for .nam files (default: ~/.pedal/models)"
|
||||
echo " IR_DIR target for .wav IRs (default: ~/.pedal/irs)"
|
||||
;;
|
||||
*)
|
||||
err "Unknown option: $1"
|
||||
echo "Usage: $0 [--list|--models|--irs|--all|--help]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,812 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate 48 factory presets (12 banks x 4 presets) for Pi Multi-FX Pedal.
|
||||
|
||||
Replaces ALL existing factory presets in presets/factory/.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
|
||||
FACTORY_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "presets", "factory")
|
||||
|
||||
# ── Banks ──────────────────────────────────────────────────────────────
|
||||
BANKS = [
|
||||
{"number": 0, "name": "Classic Rock"},
|
||||
{"number": 1, "name": "Punk"},
|
||||
{"number": 2, "name": "Metal"},
|
||||
{"number": 3, "name": "Hardcore"},
|
||||
{"number": 4, "name": "Emo"},
|
||||
{"number": 5, "name": "Midwest Emo"},
|
||||
{"number": 6, "name": "Djent"},
|
||||
{"number": 7, "name": "Acoustic"},
|
||||
{"number": 8, "name": "Grunge"},
|
||||
{"number": 9, "name": "Alternative Rock"},
|
||||
{"number": 10, "name": "Garage Rock"},
|
||||
{"number": 11, "name": "Pop"},
|
||||
]
|
||||
|
||||
# ── IR path shortcuts ─────────────────────────────────────────────────
|
||||
IR = {
|
||||
"vintage": "~/.pedal/irs/vintage-1x12.wav",
|
||||
"british": "~/.pedal/irs/british-4x12.wav",
|
||||
"american": "~/.pedal/irs/american-2x12.wav",
|
||||
"modern": "~/.pedal/irs/modern-4x12.wav",
|
||||
"jazz": "~/.pedal/irs/jazz-1x15.wav",
|
||||
"boutique": "~/.pedal/irs/boutique-1x12.wav",
|
||||
"mini": "~/.pedal/irs/mini-1x8.wav",
|
||||
}
|
||||
|
||||
# ── Helper builders ────────────────────────────────────────────────────
|
||||
|
||||
def noise_gate(threshold=0.01):
|
||||
return {"fx_type": "noise_gate", "enabled": True, "bypass": False,
|
||||
"params": {"threshold": threshold}, "nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def compressor(threshold=0.3, ratio=4.0, gain=0.8):
|
||||
return {"fx_type": "compressor", "enabled": True, "bypass": False,
|
||||
"params": {"threshold": threshold, "ratio": ratio, "gain": gain},
|
||||
"nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def boost(gain_db=6.0):
|
||||
return {"fx_type": "boost", "enabled": True, "bypass": False,
|
||||
"params": {"gain_db": gain_db}, "nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def overdrive(drive=0.5, gain=0.7, tone=0.5):
|
||||
return {"fx_type": "overdrive", "enabled": True, "bypass": False,
|
||||
"params": {"drive": drive, "tone": tone, "gain": gain},
|
||||
"nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def distortion(drive=0.7, gain=0.5):
|
||||
return {"fx_type": "distortion", "enabled": True, "bypass": False,
|
||||
"params": {"drive": drive, "gain": gain},
|
||||
"nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def fuzz(gain=0.6):
|
||||
return {"fx_type": "fuzz", "enabled": True, "bypass": False,
|
||||
"params": {"gain": gain}, "nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def eq(bass=0.5, mid=0.5, treble=0.5):
|
||||
return {"fx_type": "eq", "enabled": True, "bypass": False,
|
||||
"params": {"bass": bass, "mid": mid, "treble": treble},
|
||||
"nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def ir_cab(ir_path):
|
||||
return {"fx_type": "ir_cab", "enabled": True, "bypass": False,
|
||||
"params": {"wet": 1.0, "dry": 0.0},
|
||||
"nam_model_path": "", "ir_file_path": ir_path}
|
||||
|
||||
def delay(time=400.0, feedback=0.3, mix=0.25):
|
||||
return {"fx_type": "delay", "enabled": True, "bypass": False,
|
||||
"params": {"time": time, "feedback": feedback, "mix": mix},
|
||||
"nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def reverb(decay=0.4, mix=0.25):
|
||||
return {"fx_type": "reverb", "enabled": True, "bypass": False,
|
||||
"params": {"decay": decay, "mix": mix},
|
||||
"nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def chorus(rate=0.35, depth=0.5, mix=0.3):
|
||||
return {"fx_type": "chorus", "enabled": True, "bypass": False,
|
||||
"params": {"rate": rate, "depth": depth, "mix": mix},
|
||||
"nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def phaser(rate=0.3, depth=0.5, feedback=0.3):
|
||||
return {"fx_type": "phaser", "enabled": True, "bypass": False,
|
||||
"params": {"rate": rate, "depth": depth, "feedback": feedback},
|
||||
"nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def flanger(rate=0.2, depth=0.4, feedback=0.3):
|
||||
return {"fx_type": "flanger", "enabled": True, "bypass": False,
|
||||
"params": {"rate": rate, "depth": depth, "feedback": feedback},
|
||||
"nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def tremolo(rate=3.0, depth=0.4):
|
||||
return {"fx_type": "tremolo", "enabled": True, "bypass": False,
|
||||
"params": {"rate": rate, "depth": depth},
|
||||
"nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def volume(level=0.85):
|
||||
return {"fx_type": "volume", "enabled": True, "bypass": False,
|
||||
"params": {"level": level}, "nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
def octaver(mix=0.4):
|
||||
return {"fx_type": "octaver", "enabled": True, "bypass": False,
|
||||
"params": {"mix": mix}, "nam_model_path": "", "ir_file_path": ""}
|
||||
|
||||
# ── Preset definitions per bank ────────────────────────────────────────
|
||||
|
||||
PRESETS = {
|
||||
0: [ # Classic Rock
|
||||
{
|
||||
"name": "Brown Sound Crunch",
|
||||
"master_volume": 0.75,
|
||||
"chain": [
|
||||
noise_gate(0.015),
|
||||
overdrive(0.55, 0.75, 0.5),
|
||||
eq(0.55, 0.35, 0.65),
|
||||
delay(380, 0.25, 0.2),
|
||||
ir_cab(IR["british"]),
|
||||
volume(0.78),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Singing Lead",
|
||||
"master_volume": 0.85,
|
||||
"chain": [
|
||||
noise_gate(0.02),
|
||||
compressor(0.35, 3.5, 1.1),
|
||||
overdrive(0.7, 0.65, 0.6),
|
||||
distortion(0.5, 0.5),
|
||||
eq(0.5, 0.25, 0.75),
|
||||
delay(450, 0.35, 0.3),
|
||||
reverb(0.5, 0.25),
|
||||
ir_cab(IR["british"]),
|
||||
volume(0.88),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Sparkling Clean",
|
||||
"master_volume": 0.78,
|
||||
"chain": [
|
||||
noise_gate(0.008),
|
||||
compressor(0.25, 4.0, 0.85),
|
||||
eq(0.45, 0.5, 0.65),
|
||||
chorus(0.3, 0.45, 0.3),
|
||||
reverb(0.35, 0.2),
|
||||
ir_cab(IR["vintage"]),
|
||||
volume(0.82),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Hard Rock Pounder",
|
||||
"master_volume": 0.7,
|
||||
"chain": [
|
||||
noise_gate(0.025),
|
||||
boost(8.0),
|
||||
distortion(0.75, 0.45),
|
||||
eq(0.6, 0.25, 0.7),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.72),
|
||||
],
|
||||
},
|
||||
],
|
||||
1: [ # Punk
|
||||
{
|
||||
"name": "Fast Riff Machine",
|
||||
"master_volume": 0.72,
|
||||
"chain": [
|
||||
noise_gate(0.02),
|
||||
boost(6.0),
|
||||
overdrive(0.6, 0.7, 0.45),
|
||||
eq(0.5, 0.3, 0.7),
|
||||
ir_cab(IR["british"]),
|
||||
volume(0.75),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Snotty Lead",
|
||||
"master_volume": 0.82,
|
||||
"chain": [
|
||||
noise_gate(0.015),
|
||||
overdrive(0.4, 0.85, 0.4),
|
||||
distortion(0.65, 0.5),
|
||||
eq(0.4, 0.35, 0.8),
|
||||
delay(350, 0.2, 0.2),
|
||||
ir_cab(IR["british"]),
|
||||
volume(0.85),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Jangle Pop Clean",
|
||||
"master_volume": 0.76,
|
||||
"chain": [
|
||||
noise_gate(0.008),
|
||||
compressor(0.3, 4.5, 0.75),
|
||||
eq(0.4, 0.55, 0.7),
|
||||
chorus(0.4, 0.35, 0.25),
|
||||
reverb(0.3, 0.2),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.8),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Hardcore Attack",
|
||||
"master_volume": 0.68,
|
||||
"chain": [
|
||||
noise_gate(0.03),
|
||||
boost(9.0),
|
||||
distortion(0.8, 0.4),
|
||||
eq(0.55, 0.2, 0.75),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.7),
|
||||
],
|
||||
},
|
||||
],
|
||||
2: [ # Metal
|
||||
{
|
||||
"name": "Chug Chug",
|
||||
"master_volume": 0.65,
|
||||
"chain": [
|
||||
noise_gate(0.035),
|
||||
boost(10.0),
|
||||
distortion(0.85, 0.35),
|
||||
eq(0.65, 0.15, 0.7),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.68),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Scream Lead",
|
||||
"master_volume": 0.82,
|
||||
"chain": [
|
||||
noise_gate(0.03),
|
||||
compressor(0.4, 3.0, 1.3),
|
||||
boost(8.0),
|
||||
distortion(0.75, 0.4),
|
||||
eq(0.5, 0.2, 0.85),
|
||||
delay(420, 0.3, 0.28),
|
||||
reverb(0.4, 0.2),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.86),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Crystal Clean",
|
||||
"master_volume": 0.75,
|
||||
"chain": [
|
||||
noise_gate(0.008),
|
||||
compressor(0.2, 5.0, 0.7),
|
||||
eq(0.4, 0.5, 0.65),
|
||||
chorus(0.3, 0.5, 0.35),
|
||||
reverb(0.45, 0.3),
|
||||
ir_cab(IR["vintage"]),
|
||||
volume(0.78),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Doom Descent",
|
||||
"master_volume": 0.6,
|
||||
"chain": [
|
||||
noise_gate(0.04),
|
||||
boost(12.0),
|
||||
fuzz(0.75),
|
||||
distortion(0.9, 0.3),
|
||||
eq(0.7, 0.1, 0.65),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.62),
|
||||
],
|
||||
},
|
||||
],
|
||||
3: [ # Hardcore
|
||||
{
|
||||
"name": "Punch Drunk",
|
||||
"master_volume": 0.7,
|
||||
"chain": [
|
||||
noise_gate(0.025),
|
||||
overdrive(0.5, 0.8, 0.4),
|
||||
distortion(0.6, 0.5),
|
||||
eq(0.55, 0.25, 0.7),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.73),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Vein Lead",
|
||||
"master_volume": 0.8,
|
||||
"chain": [
|
||||
noise_gate(0.02),
|
||||
compressor(0.35, 3.5, 1.0),
|
||||
boost(7.0),
|
||||
overdrive(0.65, 0.6, 0.5),
|
||||
eq(0.5, 0.2, 0.8),
|
||||
delay(380, 0.25, 0.25),
|
||||
ir_cab(IR["british"]),
|
||||
volume(0.83),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Breather Clean",
|
||||
"master_volume": 0.74,
|
||||
"chain": [
|
||||
noise_gate(0.008),
|
||||
compressor(0.28, 4.0, 0.8),
|
||||
eq(0.45, 0.55, 0.6),
|
||||
reverb(0.4, 0.25),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.76),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Pit Stomp",
|
||||
"master_volume": 0.66,
|
||||
"chain": [
|
||||
noise_gate(0.035),
|
||||
boost(9.0),
|
||||
distortion(0.85, 0.35),
|
||||
eq(0.6, 0.15, 0.75),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.68),
|
||||
],
|
||||
},
|
||||
],
|
||||
4: [ # Emo
|
||||
{
|
||||
"name": "Twinkle Crunch",
|
||||
"master_volume": 0.72,
|
||||
"chain": [
|
||||
noise_gate(0.012),
|
||||
compressor(0.3, 4.0, 0.8),
|
||||
overdrive(0.35, 0.8, 0.55),
|
||||
chorus(0.4, 0.3, 0.2),
|
||||
eq(0.4, 0.6, 0.65),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.75),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Heartstring Lead",
|
||||
"master_volume": 0.84,
|
||||
"chain": [
|
||||
noise_gate(0.015),
|
||||
compressor(0.3, 3.5, 1.0),
|
||||
overdrive(0.6, 0.7, 0.5),
|
||||
eq(0.45, 0.35, 0.75),
|
||||
delay(480, 0.4, 0.35),
|
||||
reverb(0.6, 0.3),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.86),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Diary Pages",
|
||||
"master_volume": 0.76,
|
||||
"chain": [
|
||||
noise_gate(0.008),
|
||||
compressor(0.25, 5.0, 0.7),
|
||||
eq(0.35, 0.6, 0.55),
|
||||
reverb(0.55, 0.3),
|
||||
ir_cab(IR["vintage"]),
|
||||
volume(0.78),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Pop Core",
|
||||
"master_volume": 0.7,
|
||||
"chain": [
|
||||
noise_gate(0.02),
|
||||
boost(7.0),
|
||||
distortion(0.7, 0.45),
|
||||
eq(0.5, 0.25, 0.7),
|
||||
delay(350, 0.2, 0.15),
|
||||
ir_cab(IR["british"]),
|
||||
volume(0.72),
|
||||
],
|
||||
},
|
||||
],
|
||||
5: [ # Midwest Emo
|
||||
{
|
||||
"name": "Mathy Tappy",
|
||||
"master_volume": 0.72,
|
||||
"chain": [
|
||||
noise_gate(0.01),
|
||||
compressor(0.3, 4.5, 0.75),
|
||||
overdrive(0.3, 0.85, 0.5),
|
||||
eq(0.4, 0.55, 0.6),
|
||||
delay(400, 0.2, 0.15),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.74),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Emotive Swell",
|
||||
"master_volume": 0.82,
|
||||
"chain": [
|
||||
noise_gate(0.012),
|
||||
compressor(0.28, 4.0, 0.9),
|
||||
overdrive(0.5, 0.75, 0.45),
|
||||
eq(0.4, 0.4, 0.7),
|
||||
delay(500, 0.35, 0.3),
|
||||
reverb(0.5, 0.3),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.84),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Fingerpicked Porch",
|
||||
"master_volume": 0.75,
|
||||
"chain": [
|
||||
noise_gate(0.006),
|
||||
compressor(0.2, 5.0, 0.7),
|
||||
eq(0.35, 0.65, 0.55),
|
||||
chorus(0.25, 0.35, 0.2),
|
||||
reverb(0.4, 0.22),
|
||||
ir_cab(IR["boutique"]),
|
||||
volume(0.77),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Power Chord Wall",
|
||||
"master_volume": 0.68,
|
||||
"chain": [
|
||||
noise_gate(0.02),
|
||||
overdrive(0.55, 0.7, 0.45),
|
||||
distortion(0.6, 0.5),
|
||||
eq(0.5, 0.3, 0.65),
|
||||
ir_cab(IR["british"]),
|
||||
volume(0.7),
|
||||
],
|
||||
},
|
||||
],
|
||||
6: [ # Djent
|
||||
{
|
||||
"name": "Machine Gun",
|
||||
"master_volume": 0.62,
|
||||
"chain": [
|
||||
noise_gate(0.04),
|
||||
compressor(0.4, 6.0, 1.0),
|
||||
boost(12.0),
|
||||
distortion(0.85, 0.3),
|
||||
eq(0.7, 0.1, 0.8),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.65),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Polyphonic Lead",
|
||||
"master_volume": 0.8,
|
||||
"chain": [
|
||||
noise_gate(0.025),
|
||||
compressor(0.35, 4.0, 1.1),
|
||||
overdrive(0.6, 0.6, 0.55),
|
||||
eq(0.55, 0.2, 0.8),
|
||||
delay(440, 0.3, 0.28),
|
||||
reverb(0.45, 0.2),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.82),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Clean Arpeggio",
|
||||
"master_volume": 0.76,
|
||||
"chain": [
|
||||
noise_gate(0.008),
|
||||
compressor(0.25, 5.0, 0.7),
|
||||
eq(0.4, 0.5, 0.6),
|
||||
chorus(0.3, 0.4, 0.3),
|
||||
delay(480, 0.25, 0.2),
|
||||
reverb(0.4, 0.25),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.78),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Syncopated Riff",
|
||||
"master_volume": 0.6,
|
||||
"chain": [
|
||||
noise_gate(0.045),
|
||||
boost(10.0),
|
||||
fuzz(0.7),
|
||||
distortion(0.8, 0.3),
|
||||
eq(0.65, 0.1, 0.75),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.62),
|
||||
],
|
||||
},
|
||||
],
|
||||
7: [ # Acoustic
|
||||
{
|
||||
"name": "Body Strum",
|
||||
"master_volume": 0.78,
|
||||
"chain": [
|
||||
noise_gate(0.005),
|
||||
compressor(0.25, 4.5, 0.65),
|
||||
eq(0.5, 0.6, 0.55),
|
||||
reverb(0.35, 0.2),
|
||||
ir_cab(IR["boutique"]),
|
||||
volume(0.8),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Fingerpicking Lead",
|
||||
"master_volume": 0.82,
|
||||
"chain": [
|
||||
noise_gate(0.005),
|
||||
compressor(0.2, 5.0, 0.7),
|
||||
eq(0.4, 0.65, 0.5),
|
||||
delay(380, 0.2, 0.18),
|
||||
reverb(0.45, 0.25),
|
||||
ir_cab(IR["boutique"]),
|
||||
volume(0.84),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Ambient Soundscape",
|
||||
"master_volume": 0.8,
|
||||
"chain": [
|
||||
noise_gate(0.005),
|
||||
compressor(0.3, 4.0, 0.8),
|
||||
eq(0.4, 0.5, 0.55),
|
||||
chorus(0.2, 0.5, 0.4),
|
||||
reverb(0.7, 0.4),
|
||||
ir_cab(IR["jazz"]),
|
||||
volume(0.82),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Percussive Slap",
|
||||
"master_volume": 0.74,
|
||||
"chain": [
|
||||
noise_gate(0.006),
|
||||
compressor(0.35, 6.0, 0.6),
|
||||
boost(4.0),
|
||||
eq(0.55, 0.4, 0.65),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.76),
|
||||
],
|
||||
},
|
||||
],
|
||||
8: [ # Grunge
|
||||
{
|
||||
"name": "Fuzz Festival",
|
||||
"master_volume": 0.7,
|
||||
"chain": [
|
||||
noise_gate(0.015),
|
||||
overdrive(0.5, 0.75, 0.4),
|
||||
fuzz(0.6),
|
||||
eq(0.55, 0.2, 0.7),
|
||||
ir_cab(IR["british"]),
|
||||
volume(0.72),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Biting Solo",
|
||||
"master_volume": 0.82,
|
||||
"chain": [
|
||||
noise_gate(0.02),
|
||||
boost(7.0),
|
||||
distortion(0.65, 0.5),
|
||||
eq(0.35, 0.15, 0.85),
|
||||
delay(400, 0.3, 0.28),
|
||||
ir_cab(IR["british"]),
|
||||
volume(0.85),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Grunge Clean",
|
||||
"master_volume": 0.75,
|
||||
"chain": [
|
||||
noise_gate(0.008),
|
||||
compressor(0.3, 4.0, 0.8),
|
||||
overdrive(0.2, 0.9, 0.5),
|
||||
phaser(0.25, 0.4, 0.3),
|
||||
eq(0.45, 0.5, 0.6),
|
||||
ir_cab(IR["vintage"]),
|
||||
volume(0.77),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Wall of Noise",
|
||||
"master_volume": 0.65,
|
||||
"chain": [
|
||||
noise_gate(0.025),
|
||||
boost(10.0),
|
||||
fuzz(0.8),
|
||||
distortion(0.7, 0.35),
|
||||
eq(0.6, 0.1, 0.75),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.67),
|
||||
],
|
||||
},
|
||||
],
|
||||
9: [ # Alternative Rock
|
||||
{
|
||||
"name": "Indie Crunch",
|
||||
"master_volume": 0.74,
|
||||
"chain": [
|
||||
noise_gate(0.012),
|
||||
overdrive(0.45, 0.78, 0.55),
|
||||
eq(0.5, 0.4, 0.65),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.76),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Quirky Lead",
|
||||
"master_volume": 0.84,
|
||||
"chain": [
|
||||
noise_gate(0.015),
|
||||
compressor(0.3, 3.5, 1.0),
|
||||
overdrive(0.55, 0.65, 0.5),
|
||||
flanger(0.15, 0.35, 0.25),
|
||||
eq(0.45, 0.3, 0.75),
|
||||
delay(420, 0.3, 0.28),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.86),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Jangle Box",
|
||||
"master_volume": 0.76,
|
||||
"chain": [
|
||||
noise_gate(0.008),
|
||||
compressor(0.25, 4.5, 0.75),
|
||||
eq(0.4, 0.55, 0.65),
|
||||
tremolo(4.0, 0.35),
|
||||
reverb(0.3, 0.2),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.78),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Alt Heavy",
|
||||
"master_volume": 0.68,
|
||||
"chain": [
|
||||
noise_gate(0.025),
|
||||
boost(8.0),
|
||||
distortion(0.75, 0.4),
|
||||
octaver(0.3),
|
||||
eq(0.55, 0.2, 0.75),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.7),
|
||||
],
|
||||
},
|
||||
],
|
||||
10: [ # Garage Rock
|
||||
{
|
||||
"name": "Lo-Fi Crunch",
|
||||
"master_volume": 0.72,
|
||||
"chain": [
|
||||
noise_gate(0.015),
|
||||
overdrive(0.6, 0.7, 0.35),
|
||||
eq(0.5, 0.3, 0.55),
|
||||
ir_cab(IR["mini"]),
|
||||
volume(0.74),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Fuzzed Out",
|
||||
"master_volume": 0.8,
|
||||
"chain": [
|
||||
noise_gate(0.01),
|
||||
boost(6.0),
|
||||
fuzz(0.7),
|
||||
eq(0.45, 0.25, 0.65),
|
||||
delay(300, 0.3, 0.25),
|
||||
ir_cab(IR["british"]),
|
||||
volume(0.82),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Raw Clean",
|
||||
"master_volume": 0.78,
|
||||
"chain": [
|
||||
noise_gate(0.006),
|
||||
eq(0.45, 0.55, 0.5),
|
||||
tremolo(2.5, 0.3),
|
||||
reverb(0.3, 0.18),
|
||||
ir_cab(IR["vintage"]),
|
||||
volume(0.8),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Sludge Fuzz",
|
||||
"master_volume": 0.62,
|
||||
"chain": [
|
||||
noise_gate(0.03),
|
||||
boost(12.0),
|
||||
fuzz(0.85),
|
||||
distortion(0.7, 0.3),
|
||||
eq(0.65, 0.1, 0.6),
|
||||
ir_cab(IR["modern"]),
|
||||
volume(0.64),
|
||||
],
|
||||
},
|
||||
],
|
||||
11: [ # Pop
|
||||
{
|
||||
"name": "Clean Pop",
|
||||
"master_volume": 0.78,
|
||||
"chain": [
|
||||
noise_gate(0.006),
|
||||
compressor(0.28, 4.5, 0.7),
|
||||
overdrive(0.2, 0.9, 0.6),
|
||||
eq(0.45, 0.5, 0.6),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.8),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Catchy Hook",
|
||||
"master_volume": 0.84,
|
||||
"chain": [
|
||||
noise_gate(0.01),
|
||||
compressor(0.3, 4.0, 0.9),
|
||||
overdrive(0.4, 0.8, 0.5),
|
||||
chorus(0.35, 0.4, 0.3),
|
||||
delay(380, 0.25, 0.22),
|
||||
ir_cab(IR["american"]),
|
||||
volume(0.86),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Shimmer Clean",
|
||||
"master_volume": 0.8,
|
||||
"chain": [
|
||||
noise_gate(0.006),
|
||||
compressor(0.2, 5.0, 0.65),
|
||||
eq(0.35, 0.55, 0.65),
|
||||
chorus(0.3, 0.5, 0.35),
|
||||
reverb(0.5, 0.3),
|
||||
ir_cab(IR["vintage"]),
|
||||
volume(0.82),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Pop Punk",
|
||||
"master_volume": 0.72,
|
||||
"chain": [
|
||||
noise_gate(0.018),
|
||||
overdrive(0.5, 0.75, 0.45),
|
||||
distortion(0.55, 0.55),
|
||||
eq(0.5, 0.3, 0.7),
|
||||
delay(320, 0.15, 0.12),
|
||||
ir_cab(IR["british"]),
|
||||
volume(0.74),
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
# ── Generate all files ─────────────────────────────────────────────────
|
||||
|
||||
def generate():
|
||||
# Wipe existing factory dir
|
||||
if os.path.exists(FACTORY_DIR):
|
||||
# Remove all bank dirs but keep irs/
|
||||
for entry in os.listdir(FACTORY_DIR):
|
||||
path = os.path.join(FACTORY_DIR, entry)
|
||||
if entry != "irs" and os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
elif entry != "irs" and os.path.isfile(path):
|
||||
os.remove(path)
|
||||
|
||||
for bank in BANKS:
|
||||
num = bank["number"]
|
||||
bank_dir = os.path.join(FACTORY_DIR, f"bank_{num}")
|
||||
os.makedirs(bank_dir, exist_ok=True)
|
||||
|
||||
# bank.json
|
||||
bank_json = {
|
||||
"number": num,
|
||||
"name": bank["name"],
|
||||
"preset_count": 4,
|
||||
}
|
||||
with open(os.path.join(bank_dir, "bank.json"), "w") as f:
|
||||
json.dump(bank_json, f, indent=2)
|
||||
f.write("\n")
|
||||
|
||||
# 4 presets
|
||||
for prog in range(4):
|
||||
preset_data = PRESETS[num][prog]
|
||||
preset_json = {
|
||||
"name": preset_data["name"],
|
||||
"bank": num,
|
||||
"program": prog,
|
||||
"master_volume": preset_data["master_volume"],
|
||||
"tuner_enabled": False,
|
||||
"chain": preset_data["chain"],
|
||||
"midi_mappings": {},
|
||||
}
|
||||
path = os.path.join(bank_dir, f"preset_{prog}.json")
|
||||
with open(path, "w") as f:
|
||||
json.dump(preset_json, f, indent=2)
|
||||
f.write("\n")
|
||||
|
||||
print(f"✅ Generated {len(BANKS) * 4} presets across {len(BANKS)} banks")
|
||||
for bank in BANKS:
|
||||
print(f" Bank {bank['number']}: {bank['name']} — 4 presets")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate()
|
||||
@@ -1,10 +1,22 @@
|
||||
5150_EVH
|
||||
Diezel_VH4
|
||||
Engl_Powerball
|
||||
Fender_Bassman_59
|
||||
Fender_Twin_Clean
|
||||
Marshall_JCM800
|
||||
Mesa_Boogie_MarkV
|
||||
Orange_AD30
|
||||
Roland_Jazz_Chorus
|
||||
5150_BlockLetter_Boosted
|
||||
5152_Lead_TS9
|
||||
6505_Red_NoBoost
|
||||
BOSS_SD1_Feather
|
||||
Bogner_Ecstasy
|
||||
Engl_Savage
|
||||
Fender_TwinVerb_Clean
|
||||
Fender_TwinVerb_Vibrato
|
||||
Friedman_PowerAmp_Feather
|
||||
JCM2000_Clean
|
||||
JCM2000_Crunch
|
||||
JCM900_Lead
|
||||
Klon_Centaur_High
|
||||
MXR_M77_Feather
|
||||
Magnatone_Super59_Pushed
|
||||
Mesa_MarkIV_Lead
|
||||
Orange_Rockerverb
|
||||
Precision_Drive_Feather
|
||||
Splawn_ProMod_3rd
|
||||
TS808_Feather
|
||||
TubeScreamer_Maxed
|
||||
Vox_AC15_TopBoost
|
||||
|
||||
Reference in New Issue
Block a user