Add PUT /api/stream/phone: raw PCM audio push from a phone app

New endpoint accepts chunked, header-less, 16-bit LE stereo PCM @ 48 kHz
in the request body and writes it straight to the shared I2S sink for as
long as the connection stays open. Chosen deliberately unencoded (no
MP3/AAC/Opus decode) to keep this path as simple and low-risk as
possible — the companion app controls the encoding on its side.

Extracted the I2S TX channel that used to be owned outright by
web_radio_stream.cpp into main/esp32_i2s_sink.cpp, shared by both
producers with a simple tryAcquire()/release() exclusivity guard — web
radio streaming and a phone PCM stream would otherwise fight over the
same physical wire. web_radio_stream.cpp now acquires/releases around
each streamWhileEnabled() cycle instead of owning the channel itself.

net::PhoneStreamSink is a plain function-pointer struct (not a class
hierarchy) threaded through NetBootstrap::start() -> SetupWebServer::start()
-> HttpRouteContext, so components/net stays free of I2S driver headers;
main/phone_stream.cpp supplies the concrete functions (bound to
esp32_i2s_sink) and does the int16->ADAU 32-bit-slot conversion, batched
per chunk rather than per sample for the same reason as the web radio
stutter fix (6974095/7e65394 lineage).

Verified by build only — not confirmed live yet (board disconnected this
session); the actual phone app that will exercise this endpoint doesn't
exist yet either.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178rASQ6ZETPMUamvpoR2KR
This commit is contained in:
2026-08-18 08:03:39 +02:00
co-authored by Claude Sonnet 5
parent 39984a97d1
commit e8f79c4b79
11 changed files with 394 additions and 70 deletions
+45
View File
@@ -0,0 +1,45 @@
/**
* @file esp32_i2s_sink.hpp
* @brief Shared ESP32 -> ADAU1701 I2S TX channel (web radio / phone stream).
*
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
*
* Copyright 2026 Michele Bigi
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <cstddef>
#include <cstdint>
namespace esp32_i2s_sink {
/**
* Open the shared I2S TX channel (ESP32 as I2S slave, ADAU1701 as master)
* on first call; a no-op on later calls. Not thread-safe to call
* concurrently with itself — call once during boot.
* @return true on success.
*/
bool open();
/**
* Claim exclusive use of the channel for one producer (web radio stream or
* a phone PCM stream). Only one producer may hold it at a time, since both
* would otherwise fight over the same physical wire.
* @return true if acquired, false if another producer already holds it.
*/
bool tryAcquire();
/** Release a previously acquired claim. Safe to call even if not held. */
void release();
/**
* Write one frame's worth of already-32-bit-slot-formatted stereo samples
* (left, right pairs). Blocks until the DMA accepts the data.
* @param samples Interleaved L,R int32 samples (top-aligned 24-bit).
* @param sampleCount Number of int32 values in samples (2x frame count).
* @return true on success.
*/
bool writeSamples(const std::int32_t* samples, std::size_t sampleCount);
} // namespace esp32_i2s_sink