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:
@@ -86,6 +86,7 @@ public:
|
||||
* @param integration Application orchestration for preset recall.
|
||||
* @param ota Firmware OTA service for POST /api/system/ota.
|
||||
* @param webRadio Streaming config for GET/POST /api/streaming.
|
||||
* @param phoneStream I2S write-through for PUT /api/stream/phone.
|
||||
* @param companionChips Boot flags exposed on GET /api/health.
|
||||
* @param deviceIdentity EEPROM-derived SSID, hostname, and serial.
|
||||
* @return NetBootstrap on success, or a NetError.
|
||||
@@ -101,6 +102,7 @@ public:
|
||||
integration::IntegrationService& integration,
|
||||
ota::OtaService& ota,
|
||||
webradio::WebRadioService& webRadio,
|
||||
PhoneStreamSink& phoneStream,
|
||||
core::CompanionChipStatus companionChips,
|
||||
const core::DeviceIdentity& deviceIdentity);
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "net/NetState.hpp"
|
||||
|
||||
#include "esp_http_server.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
|
||||
namespace audio {
|
||||
@@ -58,6 +60,31 @@ struct httpd_handle;
|
||||
|
||||
namespace net {
|
||||
|
||||
/**
|
||||
* @brief PhoneStreamSink — plain function pointers over the shared I2S
|
||||
* TX channel, so net/ (protocol-only) never includes I2S driver
|
||||
* headers directly; main/esp32_i2s_sink.cpp supplies them.
|
||||
*
|
||||
* @dname PhoneStreamSink
|
||||
* @return n/a (type)
|
||||
* @pubstate All members are free functions with process lifetime; no
|
||||
* per-instance state.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-08-18
|
||||
*/
|
||||
struct PhoneStreamSink {
|
||||
/** Claim exclusive use of the sink; false if already held (e.g. by
|
||||
* the web radio stream). */
|
||||
bool (*tryAcquire)();
|
||||
/** Release a previously acquired claim. */
|
||||
void (*release)();
|
||||
/** Write one chunk of interleaved 16-bit stereo PCM @ 48 kHz.
|
||||
* @return false on an I2S write error. */
|
||||
bool (*writePcm16Stereo)(const std::int16_t* interleaved,
|
||||
std::size_t frameCount);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief HttpRouteContext — dependencies injected into HTTP handlers.
|
||||
*
|
||||
@@ -78,6 +105,7 @@ struct HttpRouteContext {
|
||||
integration::IntegrationService* integration; ///< Preset recall orchestration.
|
||||
ota::OtaService* ota; ///< Firmware OTA streaming.
|
||||
webradio::WebRadioService* webRadio; ///< Streaming config REST routes.
|
||||
PhoneStreamSink* phoneStream; ///< PUT /api/stream/phone I2S write-through.
|
||||
core::CompanionChipStatus companionChips; ///< Boot flags for /api/health.
|
||||
core::DeviceIdentity deviceIdentity; ///< EEPROM-derived unit identity.
|
||||
};
|
||||
@@ -158,6 +186,7 @@ public:
|
||||
* @param integration Application orchestration for preset recall.
|
||||
* @param ota Firmware OTA service for POST /api/system/ota.
|
||||
* @param webRadio Streaming config for GET/POST /api/streaming.
|
||||
* @param phoneStream I2S write-through for PUT /api/stream/phone.
|
||||
* @param companionChips Boot flags for GET /api/health.
|
||||
* @param deviceIdentity Unit identity for /api/health serialNumber.
|
||||
* @return Ok on success, or NetError::HttpServerStartFailed.
|
||||
@@ -174,6 +203,7 @@ public:
|
||||
integration::IntegrationService& integration,
|
||||
ota::OtaService& ota,
|
||||
webradio::WebRadioService& webRadio,
|
||||
PhoneStreamSink& phoneStream,
|
||||
core::CompanionChipStatus companionChips,
|
||||
const core::DeviceIdentity& deviceIdentity);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user