#!/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 handled by midi-bridge.service → UDP → Pd netreceive AUDIO_OPTS=(-audiooutdev 4 -audioindev 4) PD_ARGS=( -nogui -alsa -send "pd dsp 1" "${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=() 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 handled by midi-bridge.service → UDP → Pd netreceive 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 -send "pd dsp 1" "${AUDIO_OPTS[@]}" -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: $?)"