Commit Graph

5 Commits

Author SHA1 Message Date
bsncubed 7bbda0a1e8 Step 7.5a: Spotify client credentials in config/UI, write-only secrets
- Core config: cfg_mark_secret(group, key). GET /api/config returns ""
  for secret keys; a POST with "" keeps the stored value, null clears it;
  cfg_get() returns the real value. Documented in aes67-core-base.md.
- source.spotify_client_id / spotify_client_secret (secret write-only):
  each user's own Spotify developer app, needed by the maintained cspot
  fork (philippe44/cspot) since Spotify's 2025 API restrictions.
- UI: client ID field, password field for the secret ("leave empty to
  keep"), link to developer.spotify.com; enabled for spotify/auto modes.
- status.spotify_state: "no client credentials" while either is missing.
- Verified: secret never appears in GET; UI-style re-save keeps it;
  survives reboot; null clears it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 15:16:42 +10:00
bsncubed 3ebb27e4bb Step 7.4: HLS audio on AES67 (44.1 -> 48 kHz into the ring)
- decoder: s16 PCM -> stereo int32 -> esp_ae_rate_cvt 44.1 -> 48 kHz
  (32-bit, complexity 3; bypassed at 48 kHz; mono duplicated) -> ring.
  esp_audio_effects pinned to ~1.3.0 (1.4+ needs P4 rev >= 3).
- hls: each segment is downloaded completely into PSRAM (max 4 MB), then
  decoded; the connection is not held open while the decoder waits for
  ring space at playback speed.
- player: player_write() blocks while the ring is full and gives up when
  the source changes; the temporary 440 Hz producer is removed.
- Verified with Triple J Hottest: 441344 -> 480375 frames (10.008 s) and
  440320 -> 479260 (9.985 s) per segment; RTP 15000 packets, 0 gaps,
  peak -10 dBFS / RMS -22 dBFS; ring ~4.0 s, 0 underruns over ~50 s;
  heap 422 KB, PSRAM 27.5 MB free.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:52:19 +10:00
bsncubed 8a6cb53e3b Step 7.3: decode HLS TS segments to PCM (own TS demux + AAC decoder)
- main/decoder: MPEG-TS demux (packets reassembled across HTTP chunks,
  PAT -> PMT -> first ADTS-AAC PID, PES headers stripped) feeding the
  esp_audio_codec simple AAC decoder (ADTS, AAC-Plus enabled for HE-AAC
  v1/v2 variants). 7.3 only counts and logs PCM per segment.
- esp_audio_codec pinned to ~2.5.0: 2.6+ needs P4 rev >= 3 (this board
  is rev 1.3). Noted in CLAUDE.md, also for esp_audio_effects < 1.4.
- The library's combined TS decoder lost ~8% of the frames (segments
  decoded to 7.9-9.6 s, "decode error -1"); with the own demux every
  segment is sample exact: 441344 / 440320 frames = 10.008 / 9.985 s,
  matching EXTINF 10.0078 / 9.9846 (431 / 430 AAC frames), no errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:48:12 +10:00
bsncubed 852c0ffa0f Step 7.2: HLS client fetches live segments (log only)
- main/hls: task active while source.mode is hls/auto and hls_url is
  set. esp_http_client over HTTPS (IDF certificate bundle), redirects
  followed (max 5), streamed reads in 4 KB chunks to a sink callback.
- Master playlist: highest BANDWIDTH variant; URL resolution for
  absolute, host-relative and path-relative references; CRLF tolerant.
- Media playlist: TARGETDURATION, MEDIA-SEQUENCE, segments; start 3
  segments behind the live edge, fetch each new one in order, skip ahead
  if the window moved past us, reload after target/2 when nothing is new.
- Verified with Triple J Hottest (ABC, Akamai): 252 kbit/s AAC-LC
  variant chosen, 3 back-fill segments then one new ~10 s segment at a
  time, ~303 KB in ~1.25 s each; heap 439 KB, PSRAM 31.9 MB free.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:40:18 +10:00
bsncubed d3897f3179 Step 7.1: player plumbing (PSRAM ring -> AES67 TX), test tone mode
- main/audio_ring: SPSC ring of interleaved int32 frames in PSRAM
  (4 s at 48 kHz stereo), lock-free with acquire/release counters; the
  TX pull callback never blocks.
- main/player: source selection from source.mode and the pull callback
  for aes67_tx. tone -> the core's PTP-phased 1 kHz tone; off -> silence;
  hls/spotify -> ring with 1 s prefill (silence while buffering is not an
  underrun; running dry is, and prefills again). Status fields
  active_source, source_state, spotify_state, buffer_ms. source config
  applies live.
- New source.mode "tone" (validation, UI dropdown, doc) for commissioning.
- Temporary 440 Hz producer in hls mode (until the HLS player exists).
- Verified: tone 999.7 Hz -18 dBFS; off silent; hls 440.0 Hz with max
  sample step 60816 (ideal sine 60825, i.e. no discontinuities), buffer
  3983 ms, 0 underruns; spotify silent/not implemented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:36:15 +10:00