8cd796e3dd
- USB gadget (UAC1 + MIDI) with boot persistence - USB host mode with auto-detection - Pure Data synth patch with ALSA + UDP netreceive MIDI input - MIDI rawmidi-to-UDP bridge (midi-bridge.py) - LED matrix visualization (led-matrix-viz.py via arduino-router RPC) - MCU sketch for LED matrix control (Arduino_RouterBridge + ArduinoGraphics) - Conftest/OPA Rego policy validation suite for .pd files - gen_synth_pd.py: programmatic .pd file generator - System services: midi-bridge, led-matrix-viz, pd-synth-auto, usb-role-detect - Justfile with deploy, enable, diagnostic recipes - Diagnostic script (diag.sh)
153 lines
4.4 KiB
Bash
Executable File
153 lines
4.4 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
PD_DIR="/home/arduino/pd"
|
|
PATCH="$PD_DIR/synth.pd"
|
|
MODE="usb"
|
|
VERBOSE=false
|
|
|
|
die() { echo "[ERROR] $*" >&2; exit 1; }
|
|
info() { echo "[INFO] $*"; }
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-a|--auto) MODE="auto"; shift ;;
|
|
-i|--i2s) MODE="i2s"; shift ;;
|
|
-l|--loopback) MODE="loopback"; shift ;;
|
|
-v|--verbose) VERBOSE=true; shift ;;
|
|
-k|--kill) pkill -x pd 2>/dev/null && info "Pd stopped" || info "Pd not running"; exit 0 ;;
|
|
*) die "Unknown option: $1" ;;
|
|
esac
|
|
done
|
|
|
|
if ! command -v pd &>/dev/null; then
|
|
die "Pure Data not found. Install: apt install puredata"
|
|
fi
|
|
|
|
if [ ! -f "$PATCH" ]; then
|
|
die "Patch not found: $PATCH"
|
|
fi
|
|
|
|
# --- Auto-detect mode ---
|
|
if [ "$MODE" = "auto" ]; then
|
|
if [ -f /run/usb-role ]; then
|
|
ROLE=$(cat /run/usb-role)
|
|
else
|
|
ROLE=$(/usr/local/bin/detect-usb-role.sh 2>/dev/null && cat /run/usb-role || echo "gadget")
|
|
fi
|
|
|
|
case "$ROLE" in
|
|
gadget)
|
|
MODE="usb"
|
|
if ! aplay -l 2>/dev/null | grep -q "UAC1Gadget"; then
|
|
info "Configuring USB gadget..."
|
|
/usr/local/bin/configure-usb-audio.sh start 2>/dev/null || true
|
|
sleep 1
|
|
fi
|
|
;;
|
|
host)
|
|
if [ -f /run/usb-audio-dev ] && [ -s /run/usb-audio-dev ]; then
|
|
ALSA_DEV=$(cat /run/usb-audio-dev)
|
|
else
|
|
info "Detecting USB audio interface..."
|
|
/usr/local/bin/configure-usb-host.sh 2>/dev/null || {
|
|
die "No USB audio interface found. Plug one in via hub."
|
|
}
|
|
ALSA_DEV=$(cat /run/usb-audio-dev)
|
|
fi
|
|
info "Mode: USB host (MIDI controller + USB audio interface)"
|
|
MIDI_OPTS=(-alsamidi)
|
|
midi_dev=$(amidi -l 2>/dev/null | grep -v "^Dir" | head -1 | awk '{print $2}' | sed 's/\(hw:[0-9]*,[0-9]*\).*/\1/')
|
|
if [ -n "$midi_dev" ]; then
|
|
MIDI_OPTS+=(-midiadddev "$midi_dev")
|
|
info " MIDI device: $midi_dev"
|
|
fi
|
|
AUDIO_OPTS=(-audiooutdev 4 -audioindev 4)
|
|
PD_ARGS=(
|
|
-nogui -alsa
|
|
-send "pd dsp 1"
|
|
"${MIDI_OPTS[@]}"
|
|
"${AUDIO_OPTS[@]}"
|
|
-audiobuf 20 -blocksize 64 -r 48000
|
|
-channels 2 -inchannels 0 -outchannels 2
|
|
-path "$PD_DIR" "$PATCH"
|
|
)
|
|
if [ "$VERBOSE" = true ]; then pd "${PD_ARGS[@]}"
|
|
else pd "${PD_ARGS[@]}" > /dev/null 2>&1 &
|
|
PID=$!; info "Pd started (PID: $PID)"; info " Stop: kill $PID"
|
|
fi
|
|
exit 0
|
|
;;
|
|
*)
|
|
info "Auto-detect: unknown role '$ROLE', falling back to gadget"
|
|
MODE="usb"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# --- Configure per-mode options ---
|
|
AUDIO_OPTS=()
|
|
MIDI_OPTS=(-alsamidi)
|
|
|
|
if [ "$MODE" = "usb" ]; then
|
|
info "Mode: USB gadget (UAC1 audio + gmidi MIDI)"
|
|
AUDIO_OPTS=(-audiooutdev 2 -audioindev 2)
|
|
if aplay -l 2>/dev/null | grep -q "UAC1Gadget"; then
|
|
info " USB gadget: OK"
|
|
midi_dev=$(amidi -l 2>/dev/null | grep "f_midi" | head -1 | awk '{print $2}')
|
|
if [ -n "$midi_dev" ]; then
|
|
MIDI_OPTS+=(-midiadddev "$midi_dev")
|
|
info " MIDI device: $midi_dev"
|
|
fi
|
|
else
|
|
info " USB gadget: NOT DETECTED (kernel modules needed)"
|
|
fi
|
|
fi
|
|
|
|
if [ "$MODE" = "i2s" ]; then
|
|
if [ -f /run/i2s-audio-dev ] && [ -s /run/i2s-audio-dev ]; then
|
|
ALSA_DEV=$(cat /run/i2s-audio-dev)
|
|
else
|
|
for dev in "hw:I2S,0" "hw:MI2S,0" "hw:0,0"; do
|
|
if aplay -l 2>/dev/null | grep -qi "${dev%,*}"; then
|
|
ALSA_DEV="$dev"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
info "Mode: I2S DAC (standalone analog audio)"
|
|
AUDIO_OPTS=(-audioadddev "$ALSA_DEV")
|
|
fi
|
|
|
|
if [ "$MODE" = "loopback" ]; then
|
|
info "Mode: ALSA loopback (testing)"
|
|
AUDIO_OPTS=(-audiodev 2)
|
|
fi
|
|
|
|
PD_ARGS=(
|
|
-nogui
|
|
-alsa
|
|
"${MIDI_OPTS[@]}"
|
|
"${AUDIO_OPTS[@]}"
|
|
-send "pd dsp 1"
|
|
-audiobuf 20
|
|
-blocksize 64
|
|
-r 48000
|
|
-channels 1
|
|
-inchannels 0
|
|
-outchannels 1
|
|
-path "$PD_DIR"
|
|
"$PATCH"
|
|
)
|
|
|
|
if [ "$VERBOSE" = true ]; then
|
|
pd "${PD_ARGS[@]}" &
|
|
else
|
|
pd "${PD_ARGS[@]}" > /dev/null 2>&1 &
|
|
fi
|
|
PID=$!
|
|
info "Pd started (PID: $PID)"
|
|
info " Stop: kill $PID"
|
|
wait $PID
|
|
info "Pd exited (code: $?)"
|