Files
pi-multifx-pedal/docs/config-audio.md
T
shawn c2071a9724 feat(audio): Focusrite 2i2 stereo JACK config for 4CM
Adds:
- AudioConfig.mode field: 'mono' (default) or 'stereo_4cm'
- capture_channels / playback_channels properties for dynamic JACK -i/-o counts
- FOCUSRITE_PROFILES dict with focusrite_2i2_3gen entry
- AudioSystem.channel_mapping_help() static method
- stereo_4cm port auto-connect wiring (4 cable method)
- Systemd service content uses dynamic channel counts
- Default config YAML includes 'mode: mono'
- 11 new tests (31 total pass)
- Focusrite 4CM wiring docs in docs/config-audio.md
2026-06-08 10:40:38 -04:00

358 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Audio Configuration — PCM1808+PCM5102 I2S HAT
> **HAT:** Custom PCM1808 ADC + PCM5102 DAC I2S audio HAT
> **RPi:** 4B, Raspberry Pi OS (Bookworm, 64-bit)
> **Kernel:** rpi-6.6.y (upstream Raspberry Pi kernel)
> **Last updated:** 2026-06-07
---
## Device Tree Overlay
The PCM1808 ADC has **no upstream overlay** in the rpi-6.6.y kernel tree. A custom overlay is provided at:
```
hardware/pcm1808-pcm5102-overlay.dts
```
### Build & Install
```bash
# Install device-tree-compiler if not present
sudo apt-get install device-tree-compiler
# Compile overlay
dtc -@ -I dts -O dtb -o pcm1808-pcm5102-overlay.dtbo \
hardware/pcm1808-pcm5102-overlay.dts
# Copy to boot overlays directory
sudo cp pcm1808-pcm5102-overlay.dtbo /boot/firmware/overlays/
# On older Pi OS: /boot/overlays/
```
This compiles to `/boot/firmware/overlays/pcm1808-pcm5102-overlay.dtbo`.
### Enable Overlay
Edit `/boot/firmware/config.txt` (or `/boot/config.txt` on older installs):
```ini
# ── Custom I2S HAT: PCM1808 ADC + PCM5102 DAC ─────────
dtoverlay=pcm1808-pcm5102-overlay
# Disable onboard audio to free I2S pins
dtparam=audio=off
# Optional: force I2S pins if overlay doesn't set them
# dtparam=i2s=on
```
### Verify Overlay Loads
```bash
# Check overlay is loaded
dmesg | grep -i "pcm1808\|pcm5102\|i2s"
# Check ALSA sees both devices
aplay -l
arecord -l
# Expected output for playback:
# card 0: pcm1808pcm5102hat [pcm1808-pcm5102-hat], device 0: ... []
# Subdevices: 1/1
# Expected output for capture:
# card 0: pcm1808pcm5102hat [pcm1808-pcm5102-hat], device 0: ... []
# Subdevices: 1/1
# Check /proc device tree
cat /proc/device-tree/sound/name # should show "pcm1808-pcm5102-hat"
```
---
## ALSA Configuration
### Default Card Setup
Create `/etc/asound.conf` (system-wide) or `~/.asoundrc` (per-user):
```conf
# ── PCM1808+PCM5102 I2S HAT ─────────────────────────
# Default playback device
pcm.!default {
type asym
playback.pcm {
type hw
card 0
device 0
}
capture.pcm {
type hw
card 0
device 0
}
}
# Hardware device aliases
pcm.i2s_playback {
type hw
card 0
device 0
}
pcm.i2s_capture {
type hw
card 0
device 0
}
# Software volume control (PCM1808/PCM5102 have no HW volume)
pcm.softvol_playback {
type softvol
slave.pcm "i2s_playback"
control {
name "Master Playback Volume"
card 0
}
min_dB -50.0
max_dB 0.0
}
# 48kHz upsampler for 44.1kHz sources (like YouTube/Spotify)
pcm.rate_convert {
type plug
slave {
pcm "softvol_playback"
rate 48000
format S24_3LE
channels 2
}
}
# Default: use rate conversion + softvol
pcm.!default {
type plug
slave.pcm "rate_convert"
}
# Control device
ctl.!default {
type hw
card 0
}
```
### Test Audio
```bash
# --- Playback test ---
# Sine wave at 440Hz (A4)
speaker-test -t sine -f 440 -l 1 -c 2
# Play a WAV file
aplay /usr/share/sounds/alsa/Front_Center.wav
# --- Capture test ---
# Record 3 seconds from ADC
arecord -d 3 -f cd -t wav test_record.wav
# Play back the recording
aplay test_record.wav
# --- Full loopback test ---
# Play tone while recording (connect DAC output to ADC input)
# Check latency: <10ms at 48kHz/128 frames
arecord -d 5 -f S24_3LE -r 48000 -c 2 loopback_test.wav &
speaker-test -t sine -f 440 -l 2
wait
aplay loopback_test.wav
```
---
## JACK Audio Configuration
The pedal DSP runs under JACK. The existing JACK config in `main.py` uses `hw:1,0` (USB). Swap to `hw:0,0` for I2S:
### JACK Start
```bash
# Kill PulseAudio (conflicts with JACK on RPi)
pulseaudio --kill
# Start JACK at 48kHz/128 frames (recommended target: ~5.3ms buffer)
jackd -R -d alsa \
-d hw:0,0 \
-r 48000 \
-p 128 \
-n 3
# For low-latency mode (risky, check xruns):
jackd -R -d alsa -d hw:0,0 -r 48000 -p 64 -n 2
```
### Update main.py Config
In `/home/oplabs/projects/pi-multifx-pedal/main.py`, change:
```python
# FROM (USB Audio):
# config = {
# "input_device": "hw:1,0",
# "output_device": "hw:1,0",
# }
# TO (I2S HAT):
config = {
"input_device": "hw:0,0", # PCM1808 ADC
"output_device": "hw:0,0", # PCM5102 DAC
"sample_rate": 48000,
"period_size": 128, # 2.6ms buffer
"num_periods": 3, # triple-buffered
}
```
---
## Integration with NAM DSP
The I2S HAT uses the **same block size** as the NAM DSP engine (256 samples):
| Parameter | Value | Note |
|-----------|-------|------|
| Sample rate | 48,000 Hz | Both JACK and NAM |
| Block size | 256 frames | NAM internal block |
| JACK period | 128 frames | 2 × periods per block |
| Round-trip (target) | <8ms | 128fr × 2 + codec delay + DSP |
At 48kHz/128 frames × 3 periods = ~8ms round-trip, well within the 10ms target for NAM DSP. The 256-sample NAM block fits exactly into 2 JACK periods.
---
## Troubleshooting
### No card 0 detected
```bash
# Check if I2S is enabled in pin controller
dmesg | grep -i i2s
# Check loaded overlays
vcgencmd get_config dtov
# Should show: pcm1808-pcm5102-overlay
# Rebuild overlay if kernel was updated
sudo dtc -@ -I dts -O dtb -o /boot/firmware/overlays/pcm1808-pcm5102-overlay.dtbo \
/home/oplabs/projects/pi-multifx-pedal/hardware/pcm1808-pcm5102-overlay.dts
```
### No capture device
PCM1808 has no upstream overlay — the custom overlay provides the `pcm1808` codec binding. If capture isn't showing:
```bash
# Verify the overlay node compiled
dtc -I dtb -O dts /boot/firmware/overlays/pcm1808-pcm5102-overlay.dtbo | grep -i pcm1808
# Check I2S pin muxing is correct
raspi-gpio get 18 19 20 21
# Expect: GPIO 18: level=0 func=ALT5 (I2S BCLK)
# GPIO 19: level=0 func=ALT5 (I2S LRCLK)
# GPIO 20: level=0 func=ALT5 (I2S DIN)
# GPIO 21: level=0 func=ALT5 (I2S DOUT)
```
### PCM5102 hiss
- Add 330Ω + 10µF RC filter on DAC output (R5, C10 in BOM)
- Ensure XSMT (pin 17) is pulled to 3.3V, not floating
- Check 3.3V rail noise with oscilloscope (<50mV ripple at audio frequencies)
### JACK xruns
- Increase period size: `-p 256` (5.3ms, safer)
- Increase num periods: `-n 3` (triple buffer)
- Isolate RPi 4B from USB power noise
- Check `cat /proc/asound/card0/pcm0p/sub0/status` for xrun count
---
## Pinout Reference
| Signal | BCM GPIO | Physical Pin | ALT Function | Direction |
|--------|----------|:------------:|:------------:|:---------:|
| BCLK | GPIO18 | Pin 12 | ALT5 (I2S) | RPi → HAT |
| LRCLK | GPIO19 | Pin 35 | ALT5 (I2S) | RPi → HAT |
| DIN | GPIO20 | Pin 38 | ALT5 (I2S) | HAT → RPi |
| DOUT | GPIO21 | Pin 40 | ALT5 (I2S) | RPi → HAT |
| 3.3V | — | Pins 1, 17 | Power | RPi → HAT |
| 5V | — | Pins 2, 4 | Power | RPi → HAT |
| GND | — | Pins 6, 9, 14, 25, 30, 34, 39 | Ground | — |
The I2S HAT uses a **stacking header** (2×20 female socket) to pass through all 40 GPIO pins so footswitches, LEDs, and display are accessible from the top of the HAT.
---
## Focusrite Scarlett 2i2 — 4CM (Four Cable Method)
> **Interface:** Focusrite Scarlett 2i2 (3rd gen) USB audio interface
> **Mode:** `stereo_4cm` in `audio.mode`
> **Device:** `hw:1,0` (USB card index, may vary)
### Wiring
```
Guitar ─────→ Input 1 (ADC channel 0) ─→ hw:1,0 capture_0 ─→ Pipeline input_0
Amp FX Send ─→ Input 2 (ADC channel 1) ─→ hw:1,0 capture_1 ─→ Pipeline input_1
Pipeline output_0 (pre-amp) ─→ Output 1 (DAC channel 0) ─→ hw:1,0 playback_0 ─→ Amp Input (Send)
Pipeline output_1 (post-amp) ─→ Output 2 (DAC channel 1) ─→ hw:1,0 playback_1 ─→ Amp FX Return
```
### JACK Config
```bash
# Kill PulseAudio first — it grabs USB audio devices
pulseaudio --kill
# Start JACK at 48kHz/128 frames, 2 capture + 2 playback channels
jackd -R -d alsa -d hw:1,0 -r 48000 -p 128 -n 3 -C 2 -P 2
```
### Application Config
Set these fields in `~/.pedal/config.yaml`:
```yaml
audio:
hat_type: "focusrite_2i2_3gen"
profile: "standard"
mode: "stereo_4cm"
input_device: "hw:1,0"
output_device: "hw:1,0"
jack_enabled: true
auto_connect: true
```
### Port Auto-Connect
When `auto_connect: true` and mode is `stereo_4cm`, the system creates these JACK connections:
| Source | Destination | Signal |
|--------|-------------|--------|
| `system:capture_1` | `fx_in:input_0` | Guitar → pre-amp DSP |
| `system:capture_2` | `fx_in:input_1` | FX Send → post-amp DSP |
| `fx_out:output_0` | `system:playback_1` | Pre-amp DSP → amp input |
| `fx_out:output_1` | `system:playback_2` | Post-amp DSP → FX return |
### Channel Mapping Helper
At runtime, call `AudioSystem.channel_mapping_help("stereo_4cm")` to get these mappings as structured data.
### Notes
- The Focusrite 2i2 registers as ALSA card index **1** when the I2S HAT is card 0 (Linux arranges by driver probe order). If HDMI audio or other USB devices are present, the index may shift — use `aplay -l` / `arecord -l` to confirm.
- 2-in/2-out at 24-bit/48kHz. JACK runs at 24-bit by default over ALSA.
- At 48kHz/128 frames × 3 periods, round-trip latency is ~6-8ms — well within the 10ms target for real-time guitar DSP.