5c51b3db28
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.
372 lines
17 KiB
Bash
Executable File
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 "$@"
|