Skip to content

Instantly share code, notes, and snippets.

@torgeir
Last active April 19, 2026 20:10
Show Gist options
  • Select an option

  • Save torgeir/e1fc51da45d3491dd4b08ebf35710ebc to your computer and use it in GitHub Desktop.

Select an option

Save torgeir/e1fc51da45d3491dd4b08ebf35710ebc to your computer and use it in GitHub Desktop.
Raspberry pi 4 guitar pedal setup with neural-amp-modeler-lv2 and ImpulseLoader.lv2
#!/usr/bin/env bash
set -eo pipefail
# -----------------------
# Raspberry PI guitar rig
# -----------------------
# functions to log statements in color
g="\033[32m"
r="\033[31m"
o="\033[33m"
w="\033[37m"
c="\033[0m"
function log() {
echo -e "${g}$1$c"
}
function log_warn() {
echo -e "${r}$1$c"
}
function log_os() {
echo -e "${w}os: ${o}$1$c"
}
sound_cards() {
cat /proc/asound/cards \
| awk -F'[][]' '{print $2}' \
| awk 'NF' \
| tr -d ' '
}
# First available audio input card that isn't the USB gadget output.
detect_input_card() {
sound_cards | grep -v '^UAC2Gadget$' | head -1
}
# First available audio output card that isn't UAC2Gadget and isn't the input card.
# Falls back to the input card if no dedicated output exists.
detect_output_card() {
local input_card
input_card=$(cat ~/.preferred-sound-card-in 2>/dev/null)
sound_cards | grep -v '^UAC2Gadget$' | grep -v "^${input_card}$" | head -1 \
|| echo "$input_card"
}
update_os() {
log_os "running apt update.."
sudo apt update
log_os "running apt upgrade.."
sudo apt upgrade
}
ensure_deps() {
log_os "install alsa and deps"
sudo apt install -y libasound2-dev g++ cmake alsa-tools alsa-utils
log_os "lv2 plugins$c enough to build them"
sudo apt install -y build-essential git lv2-dev pkg-config libsndfile1-dev libfftw3-dev libcairo2-dev libx11-dev
log_os "install mod-host deps"
sudo apt install -y libreadline-dev liblilv-dev lilv-utils libjack-jackd2-dev
log_os "install mod-ui deps"
sudo apt install -y libjpeg-dev zlib1g-dev python3-setuptools python3-dev libfreetype6-dev authbind
log_os "install useful apps"
sudo apt install -y wget tmux git vim netcat-openbsd exa htop fzf fd-find
log_os "install ImpulseLoaderStereo's xxd$c nescessary for building it"
# https://github.com/brummer10/ImpulseLoaderStereo.lv2/issues/4
sudo apt install xxd
log_os "install lv2ls$c to list installed lv2 plugins"
sudo apt install -y lilv-utils
# lv2 plugin host https://github.com/drobilla/jalv/
#sudo apt install -y jalv
log_os "guitarix plugins lv2"
sudo apt install guitarix-lv2
# git clone https://github.com/x42/darc.lv2.git
# sudo apt-get install libcairo2-dev libpango1.0-dev libglu1-mesa-dev libgl1-mesa-dev
log_os "jack2 + jack-tools"
sudo apt install -y --no-install-recommends jackd2 jack-tools
sudo apt install linux-cpupower
log_os "disabling and stopping all unnescessary processing to prevent xruns.."
sudo systemctl disable dphys-swapfile cron bluetooth hciuart triggerhappy prometheus-node-exporter ModemManager nvmf-autoconnect openipmi smartmontools
sudo systemctl stop dphys-swapfile cron bluetooth hciuart triggerhappy prometheus-node-exporter ModemManager nvmf-autoconnect openipmi smartmontools
sudo dphys-swapfile swapoff
}
realtime_jack() {
log "configuring realtime jack"
DEBIAN_FRONTEND=noninteractive \
DEBCONF_NONINTERACTIVE_SEEN=true \
sudo dpkg-reconfigure --frontend=noninteractive -p high jackd2
echo
log_warn "you should log out and log in again after this (adjusted rtprio), or just reboot"
echo
}
deps_cleanup() {
log "deps cleanup:$c remove no longer needed deps"
sudo apt autoremove
}
create_folders() {
log "creating ~/.bin:$c scripts to run everything will be put here"
mkdir -p ~/bin
mkdir -p ~/plugs
log "creating ~/.plugs:$c plugins will be downloaded here"
mkdir -p ~/.lv2
log "creating ~/.lv2:$c plugins will be installed here"
log "$(cat <<EOF
creating ~/mod-user-files:$c the folders where you will put models and IRs go $g
# ~/mod-user-files
# |- "Aida DSP Models"
# |- "Audio Loops"
# |- "Audio Recordings"
# |- "Audio Samples"
# |- "Audio Tracks"
# |- "Hydrogen Drumkits"
# |- "MIDI Clips"
# |- "MIDI Songs"
# |- "NAM Models" $c <-- put models here $g
# |- "Reverb IRs"
# |- "SF2 Instruments"
# |- "SFZ Instruments"
# |- "Speaker Cabinets IRs" $c <-- put IRs here $c
EOF
)"
# mod-ui expects these folders
mkdir -p mod-user-files/Aida\ DSP\ Models
mkdir -p mod-user-files/Audio\ Loops
mkdir -p mod-user-files/Audio\ Recordings
mkdir -p mod-user-files/Audio\ Samples
mkdir -p mod-user-files/Audio\ Tracks
mkdir -p mod-user-files/Hydrogen\ Drumkits
mkdir -p mod-user-files/MIDI\ Clips
mkdir -p mod-user-files/MIDI\ Songs
mkdir -p mod-user-files/NAM\ Models
mkdir -p mod-user-files/Reverb\ IRs
mkdir -p mod-user-files/SF2\ Instruments
mkdir -p mod-user-files/SFZ\ Instruments
mkdir -p mod-user-files/Speaker\ Cabinets\ IRs
}
install_mod_host() {
if [ -d mod-host ]; then
log "mod-host:$c already installed"
return
fi
log "mod-host:$c downloading"
git clone --recurse-submodules -j4 https://github.com/mod-audio/mod-host.git
pushd mod-host
log "mod-host:$c compiling"
make
log "mod-host:$c installing"
sudo make install
popd
}
install_mod_ui() {
if [ -d mod-ui ]; then
log "mod-ui:$c already installed"
return
fi
log "mod-ui:$c downloading"
git clone https://github.com/moddevices/mod-ui
pushd mod-ui
log "mod-ui:$c creating venv"
python -m venv modui-env
log "mod-ui:$c activating venv"
source modui-env/bin/activate
log "mod-ui:$c installing dependencies in venv"
pip install -r requirements.txt
log "mod-ui:$c compiling"
make -C utils
log "mod-ui:$c apply crazy python3.11 tornado hack, mod-ui seems to expect python3.10"
sed -i -e 's/collections.MutableMapping/collections.abc.MutableMapping/' \
modui-env/lib/python3.11/site-packages/tornado/httputil.py
popd
}
install_mod_plugins() {
if [ ! -d plugs/mod-lv2-data ]; then
log "mod plugins:$c downloading"
git clone https://github.com/mod-audio/mod-lv2-data.git plugs/mod-lv2-data
else
log "mod plugins:$c already downloaded"
fi
# needs apt install guitarix-lv2
if [ ! -d ~/.lv2/gx_amp.lv2 ]; then
log "mod plugins:$c copy already compiled plugins into place"
# TODO
#cp -r plugs/mod-lv2-data/plugins/gx_*.lv2 ~/.lv2/
# merge and preserve existing files in ~/.lv2/
rsync -av --ignore-existing plugs/mod-lv2-data/plugins-fixed/*.lv2 ~/.lv2/
else
log "mod plugins:$c plugins already installed"
fi
}
install_impulse_loader() {
if [ ! -d plugs/ImpulseLoader.lv2 ]; then
log "impulse loader:$c downloading"
git clone --recurse-submodules -j4 https://github.com/brummer10/ImpulseLoader.lv2.git plugs/ImpulseLoader.lv2
else
log "impulse loader:$c already downloaded"
fi
if [ ! -d ~/.lv2/ImpulseLoader.lv2 ]; then
pushd plugs/ImpulseLoader.lv2
log "impulse loader:$c compiling"
make
log "impulse loader:$c installing"
cp -r bin/ImpulseLoader.lv2 ~/.lv2/
log "impulse loader:$c make mod-ui show cabsims (i.e. wav files) from 'Speaker Cabinets IRs' in the ImpulseLoader plugin"
sed -i '/^ *mod:fileTypes/s/"wav,audio"/"wav,audio,cabsim"/' ~/.lv2/ImpulseLoader.lv2/ImpulseLoader.ttl
popd
else
log "impulse loader:$c already installed"
fi
}
install_ratatouille() {
if [ ! -d plugs/Ratatouille.lv2 ]; then
log "ratatouille:$c downloading"
git clone --recurse-submodules -j4 https://github.com/brummer10/Ratatouille.lv2 plugs/Ratatouille.lv2
else
log "ratatouille:$c already downloaded"
fi
if [ ! -d ~/.lv2/Ratatouille.lv2 ]; then
pushd plugs/Ratatouille.lv2
log "ratatouille:$c compiling"
make
log "ratatouille:$c installing"
cp -r bin/Ratatouille.lv2 ~/.lv2/
popd
else
log "ratatouille:$c already installed"
fi
}
install_neural_amp_modeler() {
if [ ! -d plugs/neural-amp-modeler-lv2 ]; then
log "neural amp modeler:$c downloading"
git clone --recurse-submodules -j4 https://github.com/mikeoliphant/neural-amp-modeler-lv2 plugs/neural-amp-modeler-lv2
else
log "neural amp modeler:$c already downloaded"
fi
if [ ! -d ~/.lv2/neural_amp_modeler.lv2 ] || ! cmp -s ~/.preferred-period-frames ~/.preferred-period-frames-compiled ; then
pushd plugs/neural-amp-modeler-lv2/build
log "neural amp modeler:$c compiling"
cmake .. -DCMAKE_BUILD_TYPE="Release" -DWAVENET_FRAMES=$(cat ~/.preferred-period-frames)
make -j4
cat ~/.preferred-period-frames > ~/.preferred-period-frames-compiled
log "neural amp modeler:$c installing"
rm -rf ~/.lv2/neural_amp_modeler.lv2 1>/dev/null 2>&1
cp -r neural_amp_modeler.lv2 ~/.lv2/
popd
else
log "neural amp modeler:$c already installed"
fi
}
install_neural_rack() {
if [ ! -d plugs/NeuralRack ]; then
log "neural rack:$c downloading"
git clone --recurse-submodules -j4 https://github.com/brummer10/NeuralRack.git plugs/NeuralRack
else
log "neural rack:$c already downloaded"
fi
if [ ! -d ~/.lv2/Neuralrack.lv2 ]; then # sic
pushd plugs/NeuralRack
log "neural rack:$c submodules"
git submodule update --init --recursive
log "neural rack:$c compiling"
make lv2
log "neural rack:$c installing"
make install
popd
else
log "neural rack:$c already installed"
fi
}
install_dragonfly() {
if [ ! -d plugs/dragonfly-reverb ]; then
log "dragonfly:$c downloading"
git clone --recurse-submodules -j4 https://github.com/pedalboard/dragonfly-reverb.git plugs/dragonfly-reverb
else
log "dragonfly:$c already downloaded"
fi
if [ ! -d ~/.lv2/DragonflyHallReverb.lv2 ]; then
pushd plugs/dragonfly-reverb
log "dragonfly:$c compiling"
# make
log "dragonfly:$c patching"
for manifest in "bin/*.lv2/manifest.ttl"; do
sed -i 's/rdfs:seeAlso/rdfs:seeAlso <modgui.ttl>,/' $manifest
done
log "dragonfly:$c installing"
cp -r bin/*.lv2 ~/.lv2/
popd
else
log "dragonfly:$c already installed"
fi
}
# console.log([].slice.apply(document.querySelectorAll(".item-title")).map(a => a.href.replace("https://patchstorage.com/", "").replace(/\/$/, "")).join("\n"))
patchstorage_plugs=(
#setbfree-dsp-tonewheel-organ
#setbfree-whirl-speaker-old-version
#neural-record
#aida-x
cabinet-loader
#convolution-loader
#rumor
#bollie-delay-xt
#portal-lv2-bundle
#collisiondrive
#harmonic-exciter
#bluesbreaker
#neural-amp-modeler
#chowcentaur
#schrammel-ojd
#audiotocv-pitch
#audio-to-cv
zamtube
#zamulticompx2
#zamulticomp
#zamaximx2
#zamheadx2
zamgeq31
#zamgatex2
zamgate
zameq2
zamdelay
#zamautosat
#no-delay-line
#stereo-balance-control
#soul-force
#mapitchshift
#magigaverb
#mafreeverb
#mabitcrush
#amplitude-imposer
#3-band-splitter
3-band-eq
#edisdim
#vibrato-2
#spiral-2
#sidepass
#pressure5
#pocketverbs
#nikola
#mojo-plugin
#midside
#mackity
#galactic
#everytrim
#dyno-plugin
#debess
#console7crunch
#console7channel
#console7cascade
#console7buss
#cliponly2
#cliponly
#channel8
#capacitor2
#capacitor
#baxandall
#adclip7
#acceleration2
#rezonateur
#aether
#amp-profiler
#preamptubes
#preampimpulses
#poweramptubes
#powerampimpulses
#zamcompx2
zamcomp
midi-display
#simsam
#pitchotto
#setbfree-midi-controller
#paranoia
#mud-plugin
floaty
avocado
molot-lite-mono
#hardware-bypass
compressor-advanced
compressor-2
noise-gate-advanced
noise-gate
#spectrum-analyzer
#level-meter
#mixer-stereo
#mixer
#arpeggiator
#mod-ams-lv2-bundle
#fatfrog
#samplv1
#dragonfly-room-reverb
dragonfly-plate-reverb
dragonfly-hall-reverb
#dragonfly-early-reflections
#die-fluid-synth
#dexed
#carla-audiogain-lv2-bundle
#carla-files-lv2-bundle
#bitta
alo-plugin
#starchild
#abgate
#gxsd1
#gxsaturator
#gxquack
#gxliquiddrive
#gxknightfuzz
#gxhyperion
#gxhotbox
#gxguvnor
#gxepic
#gxbottlerocket
#gxbajatubedriver
#gxmuff
#gxmole
#gxcrybabygcb95
#gxwahwah
#metaltone
#sc_record-lv2-bundle
#gxtubescreamer
#ir-loader-cabsim
#gxultracab
#gxvbasspreamp
#zeroconvo-lv2-bundle
#instrument-tuner
#tinygain-lv2-bundle
#toggleswitch
switchtrigger4
#mda-vocinput
#mda-transient
#mda-tracker
#mda-testtone
#mda-talkbox
#mda-stereo
#mda-splitter
#mda-rezfilter
#mda-multiband
#mda-loudness
#mda-limiter
#mda-image
#mda-dynamics
#mda-dither
#mda-delay
#mda-de-ess
#mda-combo
#mda-bandisto
lowpassfilter
highpassfilter
gain-2x2
gain-plugin
#crossover-3
#crossover-2
#c-wider-stereo-image-synthesis
#c-tonestack-tone-stack-emulation
#c-spicex2
#c-spice
#c-sin-sine-wave-generator
#c-scape-stereo-delay-filters
#c-saturate
#c-platex2-stereo-in-out-versatile-plate-reverb
#c-plate-versatile-plate-reverb
#c-phaserii-mono-phaser-modulated-by-a-lorenz-fractal
#c-noisegate-attenuate-noise-resident-in-silence
#c-narrower-stereo-image-width-reduction
#c-fractal-audio-stream-from-deterministic-chaos
#c-eq10x2-10-band-equalizer
#c-eq10-10-band-equalizer
#c-compressx2-stereo-compressor
#c-compress-mono-compressor
#c-click-metronome
#c-chorusi-mono-chorus-flanger
#c-cabinetiii-idealised-loudspeaker-cabinet-emulation
#c-autofilter
#c-ampvts-tube-amp-tone-stack
#bandpassfilter
#open-big-muff
#gxautowah
#stereo-x-fade
#tap-vibrato
#tap-tubewarmth
#tap-tremolo
#tap-sigmoid-booster
#tap-rotary-speaker
#tap-reverberator
#tap-reflector
#tap-pitch-shifter
#tap-pink-fractal-noise
#tap-scaling-limiter
#tap-equalizer-bw
tap-equalizer
#tap-stereo-echo
#tap-mono-dynamics
#tap-stereo-dynamics
#tap-fractal-doubler
#tap-deesser
#tap-chorus-flanger
#tap-autopanner
#stuck-lv2-bundle
#midi-step-sequencer8x8
#midi-step-sequencer8x4
#midi-step-sequencer8x16
#midi-step-sequencer32x8
#midi-step-sequencer16x8
sooperlooper
#shiroverb
#rkr-lv2-bundle
#prefreak
#the-infamous-power-cut
#ping-pong-pan
#nekobi
#mverb
#midi-timecode-mtc-generator
#modulay
#ds1-plugin
#cv-round
#cv-range-divider
#random-generator
#cv-parameter-modulation
#cv-meter
#cv-gate
#control-to-cv
#attenuverter-booster
#cv-abs
#c-white-white-noise-generator
#c-ceo-chief-executive-oscillator
#c-cabinetiv-idealised-loudspeaker-cabinet-emulation
#multi-button-to-cv
the-infamous-mindi
#midi-generator
#midi-clock-generator
#larynx
#kars-plugin
#invada-lv2-bundle
#the-infamous-hip2b
#harmless
#gxswitched_tremolo
#gxsustainer
#gx-studio-preamp-stereo
#gxalembic
#gxslowgear
#gxshimmizita
#gxscreamingbird
#gxroom_simulator
#gxreverb-stereo
#gx_redeye-lv2-bundle
#gxrangemaster
#gxphaser
#gxoc_2
#gxmultibanddelay
#gxjcm800prest
#gxjcm800pre
#gxhornet
#gxhogsfoot
#gxhf_brightener
#gxfuzzfacefuller
#gxfuzzfacejh2
#gxfuzz
#gxfuzzmaster
#gxflanger
#gxexpander
#gxecho-stereo
gxduck_delay_st
#gxduck_delay
gxdigital_delay_st
#gxdigital_delay
#gxdetune
gxdelay-stereo
#gxcolorsound-tonebender
gxcompressor
#gxcabinet
#gxamplifier-stereo-x
#gxamplifier-x
#gxtilttone
#gxswitchlesswah
#gxbooster
#granulator-2
#freaktail
#freakclip
#fomp-lv2-bundle
#fil4-lv2-bundle
#autotune-2
#the-infamous-ewham
#calf-lv2-bundle
#setbfree-organ-reverb
#setbfree-organ-overdrive
bollie-delay
#artyfx-lv2-bundle
#amsynth
#mda-dubdelay
#mda-dx10
#mda-epiano
#mda-jx10
#mda-leslie
#mda-overdrive
#mda-piano
#mda-repsycho
#mda-ringmod
#mda-roundpan
#mda-shepard
#mda-subsynth
#mda-ambience
#mda-beatbox
#mda-degrade
#mda-detune
#mda-thruzero
#mda-vocoder
#switchbox-1-2
#gxsvt
)
install_patchstorage_plugs() {
local url filename cache_dir=~/plugs/
for plug in "${patchstorage_plugs[@]}"; do
echo $plug
if [ -f "$cache_dir/$plug" ]; then
echo " Already installed: $plug"
continue
fi
curl -s "https://patchstorage.com/$plug/" \
| grep -o 'href="[^"]*"[^>]*>.*rpi-aarch64' \
| sed 's/.*href="\([^"]*\)".*/\1/' \
| while read url; do
echo "Downloading and extracting: $url"
filename=$(basename "$url")
# Check if file already exists in cache
echo " Downloading: $url"
wget "$url" -O "$cache_dir/$plug" && \
tar xvfz "$cache_dir/$plug" -C ~/.lv2
done
done
}
install_noisegate() {
if [ ! -d plugs/noisegate ]; then
log "install noisegate:$c downloading"
git clone https://github.com/VeJa-Plugins/Noise-Gate.git plugs/noisegate
else
log "install noisegate:$c already installed"
fi
if [ ! -d ~/.lv2/noisegate.lv2 ]; then
pushd plugs/noisegate
log "noisegate:$c compiling"
make clean
make
log "noisegate:$c installing"
cp -r noisegate.lv2 ~/.lv2/
popd
else
log "noisegate:$c already installed"
fi
}
preset_model() {
if [ ! -f mod-user-files/Speaker\ Cabinets\ IRs/mesa-os-57.wav ]; then
log "neural_amp_modeler:$c fetching m100-cln.nam amp model"
wget https://github.com/torgeir/raspberry-pi-guitar/raw/refs/heads/main/m100-cln.nam \
-O mod-user-files/NAM\ Models/m100-cln.nam
else
log "neural_amp_modeler:$c already got m100-cln.nam amp model"
fi
log "neural_amp_modeler:$c creating preset model"
cat <<EOF > ~/.lv2/neural_amp_modeler.lv2/manifest.ttl
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix pset: <http://lv2plug.in/ns/ext/presets#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix state: <http://lv2plug.in/ns/ext/state#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://github.com/mikeoliphant/neural-amp-modeler-lv2>
a lv2:Plugin;
lv2:binary <neural_amp_modeler.so>;
rdfs:seeAlso <modgui.ttl>, <neural_amp_modeler.ttl>.
<model>
a pset:Preset ;
rdfs:label "model" ;
lv2:appliesTo <http://github.com/mikeoliphant/neural-amp-modeler-lv2> ;
state:state [ <http://github.com/mikeoliphant/neural-amp-modeler-lv2#model> "/home/$user/mod-user-files/NAM Models/m100-cln.nam" ] .
EOF
}
preset_ir() {
if [ ! -f mod-user-files/Speaker\ Cabinets\ IRs/mesa-os-57.wav ]; then
log "ImpulseLoader:$c fetching mesa-os-57.wav cabinet ir"
wget https://github.com/torgeir/raspberry-pi-guitar/raw/refs/heads/main/mesa-os-57.wav \
-O mod-user-files/Speaker\ Cabinets\ IRs/mesa-os-57.wav
else
log "ImpulseLoader:$c already got mesa-os-57.wav cabinet ir"
fi
log "ImpulseLoader:$c creating preset ir"
cat <<EOF > ~/.lv2/ImpulseLoader.lv2/manifest.ttl
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix pset: <http://lv2plug.in/ns/ext/presets#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix state: <http://lv2plug.in/ns/ext/state#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<urn:brummer:ImpulseLoader>
a lv2:Plugin ;
lv2:binary <ImpulseLoader.so> ;
rdfs:seeAlso <ImpulseLoader.ttl>.
<urn:brummer:ImpulseLoader#ir>
a pset:Preset ;
lv2:appliesTo <urn:brummer:ImpulseLoader> ;
rdfs:label "ir" ;
lv2:port [
lv2:symbol "Bypass" ;
pset:value 0.0
] , [
lv2:symbol "DRY_WET" ;
pset:value 100.0
] , [
lv2:symbol "INPUT" ;
pset:value 0.0
] ;
state:state [
atom:Path "/home/$user/mod-user-files/Speaker Cabinets IRs/mesa-os-57.wav"
] .
EOF
}
# https://torgeir.dev/2023/08/pwm-fan-on-raspberry-pi-4/
setup_fan() {
if [ ! -d ~/fan ]; then
log "fan:$c creating ~/fan"
mkdir -p ~/fan
cd fan
log "fan:$c creating venv"
python -m venv fan-env
log "fan:$c activating venv"
source fan-env/bin/activate
log "fan:$c installing RPi.GPIO"
pip install RPi.GPIO
else
log "fan:$c already installed"
fi
log "fan:$c writing fan.py"
cat <<'EOF' > ~/fan/fan.py
import signal
import time
import RPi.GPIO as gpio
gpio.setmode(gpio.BCM)
gpio.setup(14, gpio.OUT)
pwm = gpio.PWM(14, 50)
pwm.start(0)
def duty_for_temp(temp):
if temp < 45: return 0
if temp < 55: return 25
if temp < 65: return 50
if temp < 75: return 75
return 100
def read_temp():
with open("/sys/class/thermal/thermal_zone0/temp") as f:
return int(f.read()) / 1000.0
def signal_handler(sig, frame):
pwm.stop()
gpio.cleanup()
raise SystemExit(0)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
while True:
temp = read_temp()
duty = duty_for_temp(temp)
pwm.ChangeDutyCycle(duty)
time.sleep(5)
EOF
log "fan:$c creating ~/bin/launch-fan"
cat <<EOF > ~/bin/launch-fan
#!/usr/bin/env bash
cd /home/$user/fan
source fan-env/bin/activate
python fan.py
EOF
sudo tee /etc/systemd/system/fan.service &> /dev/null <<EOF
[Unit]
Description=pwm-fancontrol
[Service]
ExecStart=/home/$user/bin/launch-fan
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
log "fan:$c making it runnable"
chmod u+x ~/bin/launch-fan
}
setup_midi() {
if [ ! -d ~/midi ]; then
log "midi:$c creating ~/midi"
mkdir -p ~/midi
cd midi
log "midi:$c creating venv"
python -m venv midi-env
log "midi:$c activating venv"
source midi-env/bin/activate
log "midi:$c intalling RPi:GPIO"
pip install RPi.GPIO python-rtmidi evdev
cat <<EOF > midi.py
#!/usr/bin/env python
import rtmidi
#keypad
import evdev
from evdev import ecodes
import sys
cc=176 # control change
pc=192 # program change
note_off=128
note_off=144
pitch_bend=224
## see /home/nam/mod-ui/data/profile5.json, channel is set:
## "midiChannelForSnapshotsNavigation": 14,
device_path = "/dev/input/event0"
# debug keys
keys = [attr for attr in dir(ecodes) if attr.startswith('KEY_')]
#print([k for k in keys if 'BACK' in k or 'DEL' in k or 'KP' in k])
#print([k for k in keys if 'NUM' in k])
keys={
ecodes.KEY_KPPLUS: { "state": False, "send": [pc + 13, 0] },
ecodes.KEY_KPMINUS: { "state": False, "send": [pc + 13, 1] },
ecodes.KEY_KP0: { "state": False, "send": [cc + 0, 0] },
ecodes.KEY_KP1: { "state": False, "send": [cc + 0, 1] },
ecodes.KEY_KP2: { "state": False, "send": [cc + 0, 2] },
ecodes.KEY_KP3: { "state": False, "send": [cc + 0, 3] },
ecodes.KEY_KP4: { "state": False, "send": [cc + 0, 4] },
ecodes.KEY_KP5: { "state": False, "send": [cc + 0, 5] },
ecodes.KEY_KP6: { "state": False, "send": [cc + 0, 6] },
ecodes.KEY_KP7: { "state": False, "send": [cc + 0, 7] },
ecodes.KEY_KP8: { "state": False, "send": [cc + 0, 8] },
ecodes.KEY_KP9: { "state": False, "send": [cc + 0, 9] },
ecodes.KEY_KPENTER: { "state": False, "send": [cc + 0, 10] },
ecodes.KEY_KPDOT: { "state": False, "send": [cc + 0, 11] },
ecodes.KEY_KPSLASH: { "state": False, "send": [cc + 0, 12] },
ecodes.KEY_KPASTERISK: { "state": False, "send": [cc + 0, 13] },
ecodes.KEY_BACKSPACE: { "state": False, "send": [cc + 0, 14] },
ecodes.KEY_NUMLOCK: { "state": False, "send": [cc + 0, 15] }
}
def send_midi(midiout, state, send):
"""Send MIDI"""
if send[0] >= pc:
msg=send
midiout.send_message(msg)
print(f"Sent pc msg: {msg}")
elif send[0] >= cc:
if state:
value=0
else:
value=127
msg=[*send, value]
midiout.send_message(msg)
print(f"Sent cc msg: {msg}")
def main():
try:
device = evdev.InputDevice(device_path)
print(f"Connected to: {device.name}")
midiout = rtmidi.MidiOut()
if not midiout.get_ports():
midiout.open_virtual_port("Pi MIDI")
else:
midiout.open_port(0)
print("Listening for keys... Press Ctrl+C to stop")
device.grab() # Prevent normal key function
for event in device.read_loop():
print(f"event: {event}")
# is key, is handled, is up event
if event.type == ecodes.EV_KEY and event.code in keys and event.value == 0:
msg = keys[event.code]
send_midi(midiout, msg["state"], msg["send"])
msg["state"] = not msg["state"]
except KeyboardInterrupt:
print("\nStopping...")
except Exception as e:
print(f"Error: {e}")
finally:
try:
device.ungrab()
device.close()
midiout.close_port()
except:
pass
if __name__ == "__main__":
main()
EOF
else
log "midi:$c already installed"
fi
log "midi:$c creating ~/bin/launch-midi "
cat <<EOF > ~/bin/launch-midi
#!/usr/bin/env bash
cd /home/$user/midi
source midi-env/bin/activate
python midi.py
EOF
sudo tee /etc/systemd/system/midi.service &> /dev/null <<EOF
[Unit]
Description=midi
[Service]
User=$user
ExecStart=/home/$user/bin/launch-midi
[Install]
WantedBy=multi-user.target
EOF
log "midi:$c making it runnable"
chmod u+x ~/bin/launch-midi
}
create_jack() {
log "create jack.service:$c the system service that will run jack on boot"
sudo tee /etc/systemd/system/jack.service &>/dev/null <<EOF
[Unit]
Description=JACK Audio Server
After=network.target irq-affinity.service
Wants=irq-affinity.service
Before=mod-host.service mod-ui.service
[Service]
User=$user
Environment="JACK_NO_AUDIO_RESERVATION=1"
Environment="JACK_NO_DBUS=1"
Environment="JACK_DEFAULT_SERVER=default"
LimitRTPRIO=95
LimitMEMLOCK=infinity
CPUAffinity=2
CPUSchedulingPolicy=fifo
CPUSchedulingPriority=95
ExecStartPre=/bin/bash -c 'rm -rf /dev/shm/jack* /dev/shm/sem.jack* /tmp/jack* 2>/dev/null || true'
ExecStart=/home/$user/bin/launch-jack
ExecStop=/bin/bash -c 'killall -TERM jackd; sleep 2; killall -9 jackd 2>/dev/null || true'
ExecStopPost=/bin/bash -c 'rm -rf /dev/shm/jack* /dev/shm/sem.jack* /tmp/jack* 2>/dev/null || true'
StandardOutput=journal
StandardError=journal
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
log "create ~/bin/launch-jack:$c that will launch jack, to be able to route audio between plugins using mod-ui, through mod-host"
cat <<EOF > ~/bin/launch-jack
#!/usr/bin/env bash
sleep 1
hw_in=\$(cat ~/.preferred-sound-card-in)
period=\$(cat ~/.preferred-period-frames)
# Use UAC2Gadget directly when a USB host (PC) is connected and has configured
# the gadget. Fall back to preferred output (e.g. Device/speakers) otherwise.
test_is_usbgadget() {
cat /sys/class/udc/*/state 2>/dev/null | grep -q "configured"
}
if test_is_usbgadget; then
hw_out="UAC2Gadget"
echo "USB host connected - using UAC2Gadget as output"
else
hw_out=\$(cat ~/.preferred-sound-card-out)
echo "No USB host - using \$hw_out as output"
fi
echo "Starting jack with:"
echo "- input device \$hw_in"
echo "- output device \$hw_out"
echo "- period \$period"
nperiods=\$(cat ~/.preferred-nperiods 2>/dev/null || echo 3)
echo "- nperiods \$nperiods"
exec jackd -P95 -R -t4999 -d alsa -Chw:\$hw_in -Phw:\$hw_out -i 1 -o 2 -p\$period -n\$nperiods -Xseq -H -M -r48000
EOF
chmod u+x ~/bin/launch-jack
}
create_mod_services() {
sudo tee /etc/systemd/system/mod-host.service &>/dev/null <<EOF
[Unit]
Description=MOD Host Service
After=jack.service
Requires=jack.service
BindsTo=jack.service
[Service]
User=$user
LimitRTPRIO=95
LimitMEMLOCK=infinity
CPUAffinity=3
CPUSchedulingPolicy=fifo
CPUSchedulingPriority=95
ExecStartPre=/bin/sleep 4
ExecStart=mod-host -n -p 5555 -f 5556
ExecStop=/bin/bash -c 'killall -9 mod-host || true'
StandardOutput=journal
StandardError=journal
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
log "create ~/bin/launch-mod-ui:$c that will launch mod-ui, to you can build your pedalboard in the browser"
cat <<EOF > ~/bin/launch-mod-ui
#!/usr/bin/env bash
cd /home/$user/mod-ui
source modui-env/bin/activate
# https://patchstorage.com/docs/api/beta/
# PATCHSTORAGE_PLATFORM_ID=8046 is LV2 Plugins
# PATCHSTORAGE_TARGET_ID=8280 is rpi-aarch64
MOD_SOUNDCARD=\$(cat ~/.preferred-sound-card-in) \
MOD_DEV_ENVIRONMENT=0 \
MOD_USER_FILES_DIR=/home/$user/mod-user-files \
PATCHSTORAGE_API_URL=https://patchstorage.com/api/beta/patches \
PATCHSTORAGE_PLATFORM_ID=8046 \
PATCHSTORAGE_TARGET_ID=8280 \
python ./server.py
EOF
chmod u+x ~/bin/launch-mod-ui
log "run authbind:$c to allow mod-ui to start on port 80, so you can reach it without specifying the port"
sudo touch /etc/authbind/byport/80
sudo chown $user /etc/authbind/byport/80
sudo chmod 755 /etc/authbind/byport/80
sed -i "/MOD_DEVICE_WEBSERVER_PORT/s/8888/80/" ~/mod-ui/server.py
# https://github.com/SolsticeFX/pi-stomp-bookworm/blob/aeff925037e3318b078fc1418ddd86ed88d37d25/setup/mod/mod-ui.service#L25
sudo tee /etc/systemd/system/mod-ui.service &>/dev/null <<EOF
[Unit]
Description=MOD UI Service
After=network.target jack.service mod-host.service
Requires=jack.service mod-host.service
BindsTo=jack.service mod-host.service
[Service]
User=$user
ExecStartPre=/bin/sleep 3
ExecStart=/usr/bin/authbind --deep /home/$user/bin/launch-mod-ui
StandardOutput=journal
StandardError=journal
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
}
less_logging() {
log "create /etc/systemd/journald.conf:$c to configure less logging"
sudo tee /etc/systemd/journald.conf > /dev/null << 'EOF'
[Journal]
Storage=volatile
RuntimeMaxUse=32M
RuntimeMaxFileSize=8M
ForwardToConsole=yes
TTYPath=/dev/null
#MaxLevelStore=emerg
#MaxLevelSyslog=emerg
#MaxLevelKMsg=emerg
#MaxLevelConsole=emerg
#MaxLevelWall=emerg
ReadKMsg=no
Audit=no
EOF
}
usb_gadget_mode() {
log "usb gadget:$c clean up previous setup"
# Clean up any existing gadget
sudo sh -c 'echo "" > /sys/kernel/config/usb_gadget/*/UDC' 2>/dev/null || true
sudo rm -rf /sys/kernel/config/usb_gadget/* 2>/dev/null || true
sudo modprobe -r g_ether g_serial g_mass_storage g_audio libcomposite 2>/dev/null || true
log "usb gadget: create ~/bin/usb-gadget-audio:$c that will make the raspberry pi appear as a soundcard"
# Create the script (note: this will be run with sudo)
cat > ~/bin/usb-gadget-audio <<'EOF'
#!/bin/bash
# Function to clean up existing gadget
cleanup_gadget() {
echo "Cleaning up existing USB gadget..."
# Disable existing gadget
if [ -f /sys/kernel/config/usb_gadget/pi-audio/UDC ]; then
echo "" > /sys/kernel/config/usb_gadget/pi-audio/UDC 2>/dev/null || true
fi
# Remove existing gadget configuration
if [ -d /sys/kernel/config/usb_gadget/pi-audio ]; then
rm -f /sys/kernel/config/usb_gadget/pi-audio/configs/c.1/uac2.usb0 2>/dev/null || true
rmdir /sys/kernel/config/usb_gadget/pi-audio/configs/c.1/strings/0x409 2>/dev/null || true
rmdir /sys/kernel/config/usb_gadget/pi-audio/configs/c.1 2>/dev/null || true
rmdir /sys/kernel/config/usb_gadget/pi-audio/functions/uac2.usb0 2>/dev/null || true
rmdir /sys/kernel/config/usb_gadget/pi-audio/strings/0x409 2>/dev/null || true
rmdir /sys/kernel/config/usb_gadget/pi-audio 2>/dev/null || true
fi
# Clean up any other gadgets
for gadget in /sys/kernel/config/usb_gadget/*; do
if [ -d "$gadget" ]; then
echo "" > "$gadget/UDC" 2>/dev/null || true
fi
done
# Unload modules
modprobe -r g_audio libcomposite 2>/dev/null || true
# Wait a moment for cleanup
sleep 2
}
# Function to setup gadget
setup_gadget() {
echo "Setting up USB gadget..."
# Load required modules
modprobe libcomposite
cd /sys/kernel/config/usb_gadget/
mkdir -p pi-audio
cd pi-audio
echo 0x1d6b > idVendor # linux foundation
echo 0x0104 > idProduct # multifunction composite gadget
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB 2.0
mkdir -p strings/0x409
echo "fedcba9876543210" > strings/0x409/serialnumber
echo "Raspberry Pi" > strings/0x409/manufacturer
echo "Pi Audio Interface" > strings/0x409/product
mkdir -p configs/c.1/strings/0x409
echo "Audio" > configs/c.1/strings/0x409/configuration
mkdir -p functions/uac2.usb0
echo 48000 > functions/uac2.usb0/c_srate
echo 48000 > functions/uac2.usb0/p_srate
#capture,playback channel mask (binary)
echo 1 > functions/uac2.usb0/c_chmask
echo 3 > functions/uac2.usb0/p_chmask
# Increase USB request pipeline depth: more URBs queued = smoother delivery,
# less chance of ALSA buffer underrun when JACK is momentarily late.
echo 8 > functions/uac2.usb0/req_number
# Widen feedback envelope: allows larger clock-rate corrections to the host,
# reducing buffer drift between the input clock (xhci) and UAC2Gadget (PC USB clock).
echo 40 > functions/uac2.usb0/fb_max
ln -s functions/uac2.usb0 configs/c.1/
# Find and activate UDC
UDC=$(ls /sys/class/udc | head -1)
if [ -n "$UDC" ]; then
echo "$UDC" > UDC
echo "USB gadget activated with UDC: $UDC"
else
echo "Error: No UDC found"
exit 1
fi
}
# Main execution
cleanup_gadget
setup_gadget
echo "USB gadget setup complete"
EOF
chmod +x ~/bin/usb-gadget-audio
# Create cleanup script
cat > ~/bin/usb-gadget-audio-cleanup <<'EOF'
#!/bin/bash
echo "Cleaning up USB gadget on shutdown..."
# Disable all gadgets
for gadget in /sys/kernel/config/usb_gadget/*; do
if [ -d "$gadget" ] && [ -f "$gadget/UDC" ]; then
echo "" > "$gadget/UDC" 2>/dev/null || true
fi
done
# Remove gadget configuration
if [ -d /sys/kernel/config/usb_gadget/pi-audio ]; then
rm -f /sys/kernel/config/usb_gadget/pi-audio/configs/c.1/uac2.usb0 2>/dev/null || true
rmdir /sys/kernel/config/usb_gadget/pi-audio/configs/c.1/strings/0x409 2>/dev/null || true
rmdir /sys/kernel/config/usb_gadget/pi-audio/configs/c.1 2>/dev/null || true
rmdir /sys/kernel/config/usb_gadget/pi-audio/functions/uac2.usb0 2>/dev/null || true
rmdir /sys/kernel/config/usb_gadget/pi-audio/strings/0x409 2>/dev/null || true
rmdir /sys/kernel/config/usb_gadget/pi-audio 2>/dev/null || true
fi
# Unload modules
modprobe -r g_audio libcomposite 2>/dev/null || true
echo "USB gadget cleanup complete"
EOF
chmod +x ~/bin/usb-gadget-audio-cleanup
log "create usb-gadget-audio.service:$c that will set up the pi as a usb gadget on boot"
sudo tee /etc/systemd/system/usb-gadget-audio.service > /dev/null <<EOF
[Unit]
Description=USB Gadget Audio
After=network.target
Before=jack.service
[Service]
Type=oneshot
ExecStart=/home/$user/bin/usb-gadget-audio
ExecStop=/home/$user/bin/usb-gadget-audio-cleanup
RemainAfterExit=yes
TimeoutStartSec=30
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable usb-gadget-audio.service
# Run the setup immediately with sudo
log "usb gadget:$c running initial setup"
sudo /home/$user/bin/usb-gadget-audio
}
performance () {
sudo tee /etc/systemd/system/performance.service &> /dev/null <<EOF
[Unit]
Description=Performance cpu mode
After=network.target
[Service]
Type=oneshot
ExecStart=cpupower frequency-set --governor performance
RemainAfterExit=no
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable performance.service
sudo systemctl start performance.service
}
setup_irq_affinity() {
log "irq affinity:$c pin interrupts off JACK CPUs (2-3) to prevent xruns"
cat > ~/bin/irq-affinity <<'EOF'
#!/bin/bash
# Pin interrupts to CPUs 0-1, keeping CPUs 2-3 clean for JACK (isolcpus=2-3).
# Runs as root before jack.service.
pin_irq() {
local name="$1" cpus="$2" pattern="$3"
for irq in $(grep -P "$pattern" /proc/interrupts | grep -oP "^\s*\K\d+"); do
echo "$cpus" > /proc/irq/$irq/smp_affinity_list \
&& echo "Pinned IRQ $irq ($name) to CPUs $cpus"
done
}
# USB gadget (dwc2/UAC2Gadget output) — on CPU 0, fires on every audio output frame
pin_irq "dwc2/UAC2Gadget" "0" "fe980000\.usb|dwc2"
# xhci USB host controller (guitar input) — on CPU 1, separate from dwc2 on CPU 0
# so the two USB controllers never compete for the same CPU simultaneously
pin_irq "xhci" "1" "xhci_hcd"
# Ethernet — CPUs 0-1
pin_irq "eth0" "0-1" " eth0"
# PCIe PME — CPU 0
pin_irq "PCIe" "0" "PCIe PME|aerdrv"
# VideoCore mailbox + VCHIQ doorbell — CPUs 0-1
pin_irq "VC mailbox/VCHIQ" "0-1" "mailbox|VCHIQ doorbell"
# USB hub autosuspend — keep hub powered so audio input never drops mid-phrase
echo "on" > /sys/bus/usb/devices/1-1/power/control 2>/dev/null \
&& echo "USB hub 1-1 autosuspend disabled" || true
# Raise USB audio IRQ thread priorities above JACK (RT 95).
# IRQ threads default to RT 50 — JACK preempts them, delaying DMA refills → pops.
# Raising them to RT 98 ensures DMA is never starved by JACK's audio callback.
for pattern in "irq/29-xhci_hcd" "irq/39-fe980000.usb" "irq/39-dwc2"; do
for pid in $(pgrep -f "$pattern" 2>/dev/null); do
chrt -f -p 98 "$pid" && echo "Set RT 98: $pattern (pid $pid)"
done
done
EOF
chmod +x ~/bin/irq-affinity
sudo tee /etc/systemd/system/irq-affinity.service > /dev/null <<EOF
[Unit]
Description=Pin IRQs to CPUs 0-1, keeping CPUs 2-3 clean for JACK
After=network.target
Before=jack.service
[Service]
Type=oneshot
ExecStart=/home/$user/bin/irq-affinity
RemainAfterExit=yes
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable irq-affinity.service
sudo systemctl restart irq-affinity.service
}
# Configures /boot/firmware/config.txt for USB audio gadget functionality,
# and removes the default audio output and adds USB gadget, performance, and bluetooth settings.
setup_config_txt() {
log "setup /boot/firmware/config.txt:$c configuring boot settings for USB gadget audio"
sudo cp /boot/firmware/config.txt /boot/firmware/config.txt.bak
sudo sed -i.bak \
-e '/dtparam=audio=/d' \
-e '/force_turbo=/d' \
-e '/dtoverlay=/d' \
-e '/dtoverlay=disable-bt/d' \
-e '/gpu_mem=/d' \
/boot/firmware/config.txt
sudo sed -i.bak \
-e '$a dtparam=audio=off' \
-e '$a force_turbo=1' \
-e '$a dtoverlay=dwc2' \
-e '$a dtoverlay=disable-bt' \
-e '$a gpu_mem=16' \
/boot/firmware/config.txt
log_warn "/boot/firmware/config.txt:$c - reboot required for changes to take effect"
}
# Configures boot parameters /boot/firmware/cmdline.txt to enable USB audio gadget mode,
# and sets options to dont pause usb devices, and force controller speed to limit interrupts
# to improve realtime audio.
# Adds these to the default:
# modules-load=dwc2,g_audio usbcore.autosuspend=-1 dwc_otg.speed=1
setup_cmdline_txt() {
sudo cp /boot/firmware/cmdline.txt /boot/firmware/cmdline.txt.bak
sudo sed -i.bak \
-e 's/modules-load=dwc2,g_audio//g' \
-e 's/usbcore\.autosuspend=-1//g' \
-e 's/dwc_otg\.speed=1//g' \
-e 's/threadirqs//g' \
-e 's/ */ /g' \
-e 's/rootwait/rootwait modules-load=dwc2,g_audio usbcore.autosuspend=-1 dwc_otg.speed=1 threadirqs/' \
/boot/firmware/cmdline.txt
}
# not nescessary with mod-ui, it does this
#create_connect() {
# cat <<EOF > launch-connect
##!/usr/bin/env bash
#sleep 4
#jack_connect system:capture_1 effect_1:input
#jack_connect effect_1:output effect_2:in0
#jack_connect effect_2:out0 system:playback_2
#jack_lsp -c
#EOF
# chmod u+x launch-connect
#}
log "$(cat <<EOF
${o}:: Raspberry PI neural amp modeller guitar rig ::
$r
This script will $g
- update and prepare the os
- install nescessary dependencies
- configure jack for realtime audio
- download, compile and install a bunch of plugins
- mod-host
- mod-ui
- nam
- impulse loader
- effects++
- create script to launch everything
- bundle everything into a service that starts on boot
- configure the pi as an usb gadget "soundcard"
EOF
)
"
user=$USER
cd
log "-$r need:$c sudo"
log "-$r run as:$c $user"
log "-$r install a bunch of things in:$c $PWD\n"
# Only prompt when running interactively
if [ -t 0 ]; then
read -p "Press enter to proceed.."
fi
if [ ! -f ~/.pedal.deps ]; then
log_warn "Preparing OS. Remove the$c ~/.pedal.deps$r file to update or do it again.\n"
update_os
ensure_deps
realtime_jack
deps_cleanup
touch ~/.pedal.deps
setup_cmdline_txt
setup_config_txt
fi
usb_gadget_mode
if [ ! -f ~/.preferred-period-frames ] || [ -t 0 ]; then
log 'Jack number of period frames. 96 seems to work fine on a raspberry pi 4,
with low enough latency, without many pops and clicks (this also depends on
how many effects you run). You can raise this value to e.g. 128 or more if
you experience pops and clicks or look into tuning the os for less xruns.
Read more: https://wiki.linuxaudio.org/wiki/list_of_jack_frame_period_settings_ideal_for_usb_interface'
if [ -t 0 ]; then
log_warn "Choose number of jack period frames:"
echo -e "96\n128\n144\n192\n256\n512" \
| fzf --height 10% --reverse \
| tee ~/.preferred-period-frames
else
echo "96" | tee ~/.preferred-period-frames
log "Defaulting jack period frames to 96 (non-interactive)"
fi
else
log "Using existing jack period frames: $(cat ~/.preferred-period-frames)"
fi
if [ ! -f ~/.preferred-nperiods ] || [ -t 0 ]; then
log 'Jack number of periods (nperiods). Lower = less latency, higher = fewer xruns.
2 is very aggressive (~4ms buffer). 3 is a good balance (~6ms). 5 is the safe default.'
if [ -t 0 ]; then
log_warn "Choose number of jack periods:"
echo -e "2\n3\n4\n5" \
| fzf --height 10% --reverse \
| tee ~/.preferred-nperiods
else
echo "3" | tee ~/.preferred-nperiods
log "Defaulting jack nperiods to 3 (non-interactive)"
fi
else
log "Using existing jack nperiods: $(cat ~/.preferred-nperiods)"
fi
log "Found the following usb sound cards (cat /proc/asound/cards):"
cat /proc/asound/cards
if [ ! -s ~/.preferred-sound-card-in ] || [ -t 0 ]; then
if [ -t 0 ]; then
log_warn "Choose preferred input sound card:"
echo -e "$(sound_cards)" \
| fzf --height 10% --reverse \
| tr -d ' ' \
| tee ~/.preferred-sound-card-in
else
detect_input_card | tee ~/.preferred-sound-card-in
log "Defaulting input sound card to $(cat ~/.preferred-sound-card-in) (non-interactive, first non-gadget card)"
fi
else
log "Using existing input sound card: $(cat ~/.preferred-sound-card-in)"
fi
if [ ! -s ~/.preferred-sound-card-out ] || [ -t 0 ]; then
if [ -t 0 ]; then
log_warn "Choose preferred output sound card (they can be the same):"
echo -e "$(sound_cards)" \
| fzf --height 10% --reverse \
| tr -d ' ' \
| tee ~/.preferred-sound-card-out
else
detect_output_card | tee ~/.preferred-sound-card-out
log "Defaulting output sound card to $(cat ~/.preferred-sound-card-out) (non-interactive, first non-gadget non-input card)"
fi
else
log "Using existing output sound card: $(cat ~/.preferred-sound-card-out)"
fi
create_folders
install_mod_host
install_mod_ui
# amp+cab
install_impulse_loader
install_neural_amp_modeler
install_neural_rack
install_ratatouille
# plugins
# install_noisegate
install_patchstorage_plugs
#install_mod_plugins
preset_model
preset_ir
performance
setup_irq_affinity
setup_fan
setup_midi
create_jack
create_mod_services
less_logging
log "reloading existing service definitions.."
sudo systemctl daemon-reload
log "making services start on boot as well.."
sudo systemctl enable fan.service
sudo systemctl enable midi.service
sudo systemctl enable irq-affinity.service
sudo systemctl enable jack.service
sudo systemctl enable mod-host.service
sudo systemctl enable mod-ui.service
sudo systemctl enable performance.service
log "setting performance mode"
sudo systemctl restart performance.service
log "restarting fan"
sudo systemctl restart fan.service
log "restarting midi"
sudo systemctl restart midi.service
log "stopping mod-ui"
sudo systemctl stop mod-ui.service
log "stopping mod-host"
sudo systemctl stop mod-host.service
log "stopping jack"
sudo systemctl stop jack.service
log "starting jack"
sudo systemctl start jack.service
log "starting mod-host"
sudo systemctl start mod-host.service
log "starting mod-ui"
sudo systemctl start mod-ui.service
log "for each core, cpu governor is set to:"
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
log "$(cat <<EOF
Done.
$oNow what?$c
$g Reboot, if this is your first time running the script:
$c sudo reboot
$g Copy models, so mod-ui will show them in the ui, e.g. in Neural Amp Modeler:
$c scp model.nam nam@pedal.lan:/home/$user/mod-user-files/NAM\ Models/
$g Copy IRs, so mod-ui will show them in the ui, e.g. in ImpulseLoader:
$c scp cab-ir.wav nam@pedal.lan:/home/$user/mod-user-files/Speaker\ Cabinet\ IRs/
$g Stop service:
$c sudo systemctl restart pedal.service
$g Restart service:
$c sudo systemctl restart pedal.service
$g Show last logs:
$c journalctl -u pedal.service -b0
$g Tail logs:
$c journalctl -u pedal.service -f
$g Adjust overall volume:
$c alsamixer
$g Store set volume:
$c sudo alsactl store
$g It is restored on boot, or using:
$c sudo alsactl restore
Visit$c http://pedal.lan$g
Enjoy!
EOF
)\n"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment