/* * A2dp.hpp * Bluetooth A2DP (Advanced Audio Distribution Profile) * AVDTP signaling and SBC audio streaming * Copyright (c) 2026 Daniel Hammer */ #pragma once #include namespace Drivers::USB::Bluetooth::A2dp { // ========================================================================= // A2DP stream states // ========================================================================= enum class State { Idle, // No A2DP connection Discovering, // AVDTP discover in progress Configured, // Stream endpoint configured Open, // Stream open, ready for audio Streaming // Actively streaming audio }; // ========================================================================= // Public API // ========================================================================= // Drive the A2DP source role after an ACL link is up: open the AVDTP // signaling channel, negotiate an SBC stream, and open the media transport // channel. Must be called from process context (it blocks on the AVDTP // handshakes), NOT from an event/interrupt path. Leaves the stream Open. bool StartSource(uint32_t timeoutMs = 5000); // Called by L2CAP when an AVDTP channel becomes ready void OnChannelReady(uint16_t l2capCid); // Process an AVDTP signaling packet void ProcessAvdtp(const uint8_t* data, uint16_t len); // Process an SDP packet (on any SDP L2CAP channel). Dispatches by PDU: // requests (the headset querying OUR services) are answered by the minimal // SDP server; responses on our outgoing client channel complete DoSdpQuery. void ProcessSdp(uint16_t localCid, const uint8_t* data, uint16_t len); // Configure a stream for the given PCM parameters bool ConfigureStream(uint32_t sampleRate, uint8_t channels, uint8_t bitsPerSample); // Start streaming bool StartStream(); // Stop streaming. flushQueued drops any PCM still queued in the media // ring: pass true when closing the stream (track change / app exit), // false for a pause so resume continues gaplessly. bool StopStream(bool flushQueued = true); // Write PCM audio data to the Bluetooth audio stream. Copies into the // media ring and returns immediately (never blocks); returns the number // of bytes accepted (0 = ring full, retry later). int WriteAudio(const uint8_t* pcmData, uint32_t pcmLen); // Encode + send queued PCM, paced to the audio clock and gated on ACL TX // readiness. Called from the idle-loop event pump and from WriteAudio; // self-serializing, cheap no-op when not streaming. void PumpMedia(); // Get current state State GetState(); // Check if currently streaming bool IsStreaming(); // Get volume (0-100) int GetVolume(); // Set volume (0-100) void SetVolume(int percent); }