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:
2026-06-22 22:46:36 -07:00
commit 8cd796e3dd
38 changed files with 2681 additions and 0 deletions
+157
View File
@@ -0,0 +1,157 @@
# Arduino Uno Q — USB Audio Synth
A 2-oscillator subtractive synthesizer running Pure Data on the Arduino Uno Q.
**Auto-detects** its USB role at boot and works in three modes — no config switching needed.
## Modes
| Mode | Connection | Audio Out | MIDI In | Use Case |
|------|-----------|-----------|---------|----------|
| **Gadget** | USB-C → Host computer | UAC1 gadget (virtual sound card) | gmidi gadget (over same cable) | Plug-and-play with DAWs |
| **Host** | USB hub → MIDI controller + USB audio interface | USB audio interface (your interface) | MIDI controller via hub | Standalone jamming |
| **I2S DAC** | I2S DAC on GPIO pins (e.g. PCM5102) | Analog line out via DAC | USB MIDI controller (gadget) | Embedded installs |
## Auto-Detection
At boot, `detect-usb-role.sh` probes the USB controller to determine the role:
```
USB cable plugged into Host? ─→ gadget mode (UAC1 + gmidi)
USB device plugged into Uno? ─→ host mode (drives USB audio interface + MIDI)
Neither? ─→ defaults to gadget (safe fallback)
```
A udev rule (`99-usb-role.rules`) re-runs detection on hotplug events and
restarts Pd with the new config — unplug from computer and plug into a hub,
it just works.
## Architecture
```
┌─────────────────────────────────────────────────────────┐
│ GADGET MODE │
│ [Host DAW] ←USB-C→ ┌──────────┐ │
│ │ gmidi │←─ ALSA seq ──→ Pd │
│ │ UAC1 │── ALSA pcm ──→ Pd │
│ └──────────┘ │
├─────────────────────────────────────────────────────────┤
│ HOST MODE │
│ [MIDI Ctrl] ─┐ │
│ [USB Audio] ─┼─USB hub─→ ┌──────────┐ │
│ │ │ USB host │ │
│ └──────────→ │ driver │──→ Pd │
│ └──────────┘ │
├─────────────────────────────────────────────────────────┤
│ I2S DAC MODE │
│ [PCM5102] ←GPIO(I2S)→ ALSA I2S driver ──→ Pd │
└─────────────────────────────────────────────────────────┘
```
## Project Structure
```
uno-q-audio-synth/
├── pd/
│ └── synth.pd # Pure Data synthesizer patch
├── system/
│ ├── configure-usb-audio.sh # UAC1 + gmidi gadget setup
│ ├── configure-usb-host.sh # Host-mode USB audio discovery
│ ├── detect-usb-role.sh # Boot-time USB role detection
│ ├── detect-i2s-device.sh # I2S device auto-detection
│ ├── on-usb-role-change.sh # udev hotplug handler
│ ├── asound.conf # ALSA config (gadget mode)
│ ├── asound-i2s.conf # ALSA config (I2S DAC mode)
│ ├── 99-usb-role.rules # udev rules for hotplug
│ ├── usb-role-detect.service # Early-boot role detection
│ ├── usb-gadget-audio.service # Gadget-only service
│ ├── pd-synth-auto.service # Unified auto-detect service
│ ├── pd-synth.service # Gadget-only Pd service
│ ├── pd-synth-i2s.service # I2S-only Pd service
│ └── detect-i2s-device.service # I2S probe service
├── scripts/
│ └── start-synth.sh # Manual dev startup (all modes)
├── install.sh # Board-side dependency install
├── justfile # Task runner
└── mise.toml # Tool version management
```
## Quick Start
### Deploy to board
```bash
export BOARD_HOST=uno-q.local # or IP if mDNS not working
just deploy # copy all files
just enable-auto # enable auto-detect mode
just reboot # apply
```
Once booted, plug into a computer — it appears as "Uno Q Audio Synth" with
audio sink + MIDI port. Or plug a USB hub with MIDI controller + audio
interface for standalone play.
### Development cycle
```bash
just cycle # deploy patch + restart Pd
just log # tail Pd logs
just status # role, ALSA cards, MIDI ports
```
### Manual testing
```bash
just test-audio # test gadget audio out
just test-audio-host # test host-mode USB audio interface
just run-remote # run Pd in foreground with auto-detect
```
## I2S DAC Wiring
Add a DAC later without reconfiguring. Common I2S DAC modules:
| DAC | Pins | Voltage |
|-----|------|---------|
| PCM5102 | BCK, DIN, LCK, 5V, GND | 3.3V5V |
| MAX98357 (speaker amp) | BCLK, DIN, LRCLK, Vin, GND | 2.7V5.5V |
| PCM5242 (balanced out) | I2S + I2C config | 3.3V |
Typical PCM5102 wiring to Uno Q GPIO:
```
PCM5102 → Uno Q GPIO
VIN → 5V
GND → GND
BCK → I2S_BCK (GPIO pin — check board docs)
DIN → I2S_DIN
LCK → I2S_LCK
```
Activate I2S mode: `just enable-i2s && just reboot`
## Controls (MIDI)
| Control | Mapping |
|---------|---------|
| MIDI Note On/Off | Play notes |
| Velocity | Volume |
| CC 74 | Filter cutoff |
| GUI sliders (Pd) | Detune, volume, ADSR |
## Requirements (on the Uno Q)
- Pure Data (`apt install puredata`)
- ALSA utilities (`apt install alsa-utils`)
- Linux kernel with UAC + gmidi gadget support (Qualcomm DragonWing)
## Pd Patch: synth.pd
- **OSC 1**: Saw wave, direct pitch
- **OSC 2**: Saw wave, detunable (±50 cents)
- **Mix**: Balanced stereo mix
- **Filter**: Low-pass (lop~) with adjustable cutoff
- **Envelope**: env~ (attack, decay, sustain, release)
- **Output**: Stereo 48kHz 16-bit via dac~
Open in Pd GUI to adjust slider positions and save.