# RLCD Audio Apps — Mp3Player & VoiceRecorder Fixes (2026-07-12)

## Context
Waveshare RLCD 4.2" uses ES8311 DAC @0x18 + ES7210 mic ADC @0x40, MCLK=12.288MHz GPIO16, BCLK=9 WS=45 TX=8 RX=10 AMP=46 HIGH.
Working reference: `MicroPython/test1/Screen/lib/audio_util.py` + `board_config.py` WAVESHARE_RLCD profile.

## Mp3Player Pitfalls

### 1. Task Stack & Priority
- Default `xTaskCreate(..., 4096, 5)` → stack overflow under minimp3 + SD + I2S + LVGL.
- Log: `i2s_alloc_dma_desc failed` sometimes masked as OOM, but also task crashes.
- Fix: `6144, prio 6`.

### 2. taskYIELD Causing Repetition
```c
// Before: decode one MP3 frame (1152 samples @16k = 72ms) then:
taskYIELD(); // gives CPU to LVGL but DMA empties → repeats last buffer
// After: remove YIELD, loop tight, let I2S write block (250ms timeout) drive pacing
```
- DMA 4x120 = 30ms total, smaller than frame time → underrun → repetition. Increased driver DMA to 6x160 (~60ms) + removed YIELD.

### 3. Sample Rate Assumptions
- RLCD files in repo are 16k mono (faith-mysterious-garden/page001.mp3 31652 bytes, 16k 1ch).
- `I2S configured: 16000 Hz, 1 channels` — must match ES8311 boot init that already programs 16k exact.
- Reclock hook that rewrites ES8311 0x06/0x07/0x08 during I2S start causes pop. For 16k, skip entirely.
- MCLK multiple: 16k*768=12.288MHz exact, 32k*384, 48k*256. Default SDK 256 gives 4.096MHz for 16k → robot sound. See `tactility-firmware/references/rlcd-audio-fix.md`.

## VoiceRecorder Pitfalls

### Mono vs Stereo for ES7210
Working MP:
```python
i2s = I2S(1, sck=9, ws=45, sd=10, mode=I2S.RX, rate=16000, bits=16, format=I2S.STEREO)
# then mono extract: for i in 0..n step 4: mono[j]=buf[i:i+2]
```
- ES7210 outputs stereo (2 mics) even if you want mono file.
- VoiceRecorder previously requested `channel_right = NONE` (mono slot mode) → driver expects 1 slot but codec sends 2 → low pitch mic-like.
- Fix: Request `channel_right=1` (STEREO), read `AUDIO_BUF_SIZE` (4096) stereo, downmix left channel in-place backward to avoid overwrite:
```c
size_t mono_bytes = bytes_read/2;
for(int src=bytes_read-4, dst=mono_bytes-2; src>=0; src-=4, dst-=2){
  buf[dst]=buf[src]; buf[dst+1]=buf[src+1];
}
fwrite(buf,1,mono_bytes,f);
```
- WAV header stays mono (1 ch, byterate 32000) – data correctly pitched.

### TDM vs STD
- Other boards (m5stack-stackchan, tab5) use TDM 4-slot for ES7210 via `i2s_controller_set_rx_tdm_config`.
- RLCD working MP uses STD stereo, not TDM. Keep STD for RLCD. TDM path DMA also needs reduction (was 8x512 → 4x120) or OOM.

## Firmware / Logging Interaction

### File Lock Flood
`File.cpp:getLock()` logged `WARN File lock function not set!` on every `listDirectory()` – Tactility scans `/sdcard` every sec → 1Hz UART spam.
- UART ISR at 115200 contends with I2S GDMA ISR → jitter → small noises.
- Fix: once-only warning + listDir INFO→DEBUG.

### I2S Pin Logs
`esp32_i2s: Configuring I2S pins...` logged INFO on every play → same jitter.
- Fix: LOG_D.

### Verification Checklist
- Play 16k mono MP3: no pop at start, no robot, no repetition.
- Log after fix: no `File lock flood`, no `i2s_alloc_dma_desc failed`.
- Record WAV, download, play on PC: 16k mono correct pitch, not low/mic-like.
- Serial for voice: `I2S configured: 16000 Hz, 1 channels` for play, stereo read for record is internal.

## References
- `tactility-firmware/references/rlcd-audio-fix.md` – GPIO5 conflict, correct pins, DMA OOM, MCLK, ES8311 reclock
- MicroPython working code: `~/Projects/MicroPython/test1/Screen/lib/board_config.py` WAVESHARE_RLCD
