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:
2026-06-13 02:09:56 -04:00
parent 11c838a27e
commit 5c51b3db28
164 changed files with 22831 additions and 577 deletions
+508
View File
@@ -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()
+371
View File
@@ -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 "$@"
+812
View File
@@ -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()
+21 -9
View File
@@ -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