80 lines
2.9 KiB
C++
80 lines
2.9 KiB
C++
/*
|
|
* A2dp.hpp
|
|
* Bluetooth A2DP (Advanced Audio Distribution Profile)
|
|
* AVDTP signaling and SBC audio streaming
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#pragma once
|
|
#include <cstdint>
|
|
|
|
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);
|
|
|
|
}
|