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)
158 lines
6.4 KiB
Markdown
158 lines
6.4 KiB
Markdown
# 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.3V–5V |
|
||
| MAX98357 (speaker amp) | BCLK, DIN, LRCLK, Vin, GND | 2.7V–5.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.
|