Files
pi-multifx-pedal/scripts/download_nam_and_irs.sh
T
shawn 5c51b3db28 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.
2026-06-13 02:09:56 -04:00

372 lines
17 KiB
Bash
Executable File

#!/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 "$@"