Initial commit: Arduino Uno Q USB Audio Synth project
- 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)
This commit is contained in:
Executable
+152
@@ -0,0 +1,152 @@
|
||||
#!/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: $?)"
|
||||
Reference in New Issue
Block a user