98 lines
3.3 KiB
C++
98 lines
3.3 KiB
C++
/*
|
|
* Sbc.hpp
|
|
* SBC (Sub-Band Codec) encoder for Bluetooth A2DP
|
|
* Fixed-point implementation (no FPU/SSE required)
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#pragma once
|
|
#include <cstdint>
|
|
|
|
namespace Drivers::USB::Bluetooth::Sbc {
|
|
|
|
// =========================================================================
|
|
// SBC configuration
|
|
// =========================================================================
|
|
|
|
// Standard SBC parameters for A2DP
|
|
constexpr int SBC_SUBBANDS = 8;
|
|
constexpr int SBC_BLOCKS = 16;
|
|
constexpr int SBC_CHANNELS = 2; // Stereo
|
|
constexpr int SBC_BITPOOL = 53; // Standard quality
|
|
|
|
// Allocation method
|
|
constexpr uint8_t ALLOC_SNR = 0;
|
|
constexpr uint8_t ALLOC_LOUDNESS = 1;
|
|
|
|
// Channel mode
|
|
constexpr uint8_t MODE_MONO = 0;
|
|
constexpr uint8_t MODE_DUAL_CHANNEL = 1;
|
|
constexpr uint8_t MODE_STEREO = 2;
|
|
constexpr uint8_t MODE_JOINT_STEREO = 3;
|
|
|
|
// Sampling frequency
|
|
constexpr uint8_t FREQ_16000 = 0;
|
|
constexpr uint8_t FREQ_32000 = 1;
|
|
constexpr uint8_t FREQ_44100 = 2;
|
|
constexpr uint8_t FREQ_48000 = 3;
|
|
|
|
// SBC frame header
|
|
struct SbcHeader {
|
|
uint8_t SyncWord; // 0x9C
|
|
uint8_t Config; // freq(2) | blocks(2) | mode(2) | alloc(1) | subbands(1)
|
|
uint8_t Bitpool;
|
|
uint8_t Crc;
|
|
} __attribute__((packed));
|
|
|
|
// =========================================================================
|
|
// Encoder state
|
|
// =========================================================================
|
|
|
|
struct SbcEncoder {
|
|
uint8_t Frequency;
|
|
uint8_t Blocks;
|
|
uint8_t ChannelMode;
|
|
uint8_t AllocMethod;
|
|
uint8_t Subbands;
|
|
uint8_t Bitpool;
|
|
uint8_t Channels;
|
|
|
|
// Analysis filter state (per-channel windowed buffer; X[0] = newest
|
|
// sample, shifted down by Subbands each block)
|
|
int32_t X[SBC_CHANNELS][SBC_SUBBANDS * 10];
|
|
|
|
// Computed frame size in bytes
|
|
uint32_t FrameSize;
|
|
|
|
// Samples per frame
|
|
uint32_t SamplesPerFrame; // blocks * subbands
|
|
};
|
|
|
|
// =========================================================================
|
|
// Public API
|
|
// =========================================================================
|
|
|
|
// Initialize encoder with given parameters
|
|
void Init(SbcEncoder* enc, uint32_t sampleRate, uint8_t channels, uint8_t bitsPerSample);
|
|
|
|
// Override the encoder's SBC parameters from the negotiated AVDTP capability
|
|
// octets (octet0 = SamplingFreq[7:4]|ChannelMode[3:0], octet1 =
|
|
// BlockLength[7:4]|Subbands[3:2]|Alloc[1:0]) + the chosen bitpool, so the
|
|
// emitted frame headers match exactly what the sink agreed to. Call after
|
|
// Init.
|
|
void Configure(SbcEncoder* enc, uint8_t octet0, uint8_t octet1, uint8_t bitpool);
|
|
|
|
// Encode one SBC frame from PCM data
|
|
// pcm: interleaved 16-bit signed PCM, length = blocks * subbands * channels
|
|
// out: output buffer for SBC frame
|
|
// Returns number of bytes written to out
|
|
uint32_t Encode(SbcEncoder* enc, const int16_t* pcm, uint8_t* out);
|
|
|
|
// Get the frame size in bytes for the current configuration
|
|
uint32_t GetFrameSize(const SbcEncoder* enc);
|
|
|
|
// Get the number of PCM samples consumed per frame (per channel)
|
|
uint32_t GetSamplesPerFrame(const SbcEncoder* enc);
|
|
|
|
}
|