3#if defined(USE_ESP32) && defined(USE_I2S_AUDIO_SPDIF_MODE)
9static const char *
const TAG =
"i2s_audio.spdif_encoder";
15static constexpr uint8_t PREAMBLE_B = 0x17;
16static constexpr uint8_t PREAMBLE_M = 0x1d;
17static constexpr uint8_t PREAMBLE_W = 0x1b;
21static constexpr uint32_t BMC_ZERO_NIBBLE = 0x33;
26static constexpr uint16_t bmc_lut_encode(
uint32_t data, uint8_t num_bits) {
29 for (uint8_t i = 0; i < num_bits; i++) {
30 bool bit = (
data >> i) & 1;
31 uint8_t bmc_pair = phase ? (bit ? 0b01 : 0b00) : (bit ? 0b10 : 0b11);
32 bmc |=
static_cast<uint16_t
>(bmc_pair) << ((num_bits - 1 - i) * 2);
43 p ^= (value >> b) & 1u;
56static constexpr auto BMC_LUT_4 = [] {
57 std::array<uint32_t, 16>
t{};
60 uint32_t delta = bmc_lut_parity(i, 4) ? 0xFFFF0000u : 0u;
69static constexpr auto BMC_LUT_8 = [] {
70 std::array<uint32_t, 256>
t{};
73 uint32_t delta = bmc_lut_parity(i, 8) ? 0xFFFF0000u : 0u;
83 ESP_LOGE(TAG,
"Buffer allocation failed (%zu bytes)", SPDIF_BLOCK_SIZE_BYTES);
86 ESP_LOGV(TAG,
"Buffer allocated (%zu bytes)", SPDIF_BLOCK_SIZE_BYTES);
105 ESP_LOGD(TAG,
"Sample rate set to %lu Hz", (
unsigned long) sample_rate);
110 if (bytes_per_sample != 2 && bytes_per_sample != 3 && bytes_per_sample != 4) {
111 ESP_LOGE(TAG,
"Unsupported bytes per sample: %u", (
unsigned) bytes_per_sample);
119 ESP_LOGD(TAG,
"Input width set to %u-bit", (
unsigned) bytes_per_sample * 8);
154 ESP_LOGW(TAG,
"Unsupported sample rate %lu Hz, channel status will indicate 'not specified'",
168 uint8_t word_length_code;
171 word_length_code = 0x02;
175 word_length_code = 0x0D;
178 word_length_code = 0x00;
186ESPHOME_ALWAYS_INLINE
static inline uint32_t c_bit_for_frame(
const std::array<uint8_t, 24> &channel_status,
188 return static_cast<uint32_t>((channel_status[frame >> 3] >> (frame & 7)) & 1u) << 30;
206template<u
int8_t Bps> ESPHOME_ALWAYS_INLINE
static inline uint32_t build_raw_subframe(
const uint8_t *pcm_sample) {
207 static_assert(Bps == 2 || Bps == 3 || Bps == 4,
"Unsupported bytes per sample");
208 if constexpr (Bps == 2) {
210 return (
static_cast<uint32_t>(pcm_sample[1]) << 20) | (
static_cast<uint32_t>(pcm_sample[0]) << 12);
211 }
else if constexpr (Bps == 3) {
213 return (
static_cast<uint32_t>(pcm_sample[2]) << 20) | (
static_cast<uint32_t>(pcm_sample[1]) << 12) |
214 (
static_cast<uint32_t>(pcm_sample[0]) << 4);
217 return (
static_cast<uint32_t>(pcm_sample[3]) << 20) | (
static_cast<uint32_t>(pcm_sample[2]) << 12) |
218 (
static_cast<uint32_t>(pcm_sample[1]) << 4);
239ESPHOME_ALWAYS_INLINE
static inline void bmc_encode_subframe(
uint32_t raw_subframe, uint8_t preamble,
uint32_t *dst) {
240 if constexpr (Bps == 2) {
244 uint32_t nibble = (raw_subframe >> 12) & 0xF;
249 uint32_t byte_mid = (raw_subframe >> 16) & 0xFF;
250 uint32_t lut_m = BMC_LUT_8[byte_mid];
251 uint32_t bmc_16_23 = (lut_m & 0xFFFFu) ^ phase_mask;
252 phase_mask ^= lut_m >> 16;
254 uint32_t byte_hi = (raw_subframe >> 24) & 0xFF;
255 uint32_t lut_h = BMC_LUT_8[byte_hi];
256 uint32_t bmc_24_31 = (lut_h & 0xFFFFu) ^ phase_mask;
257 phase_mask ^= lut_h >> 16;
259 bmc_24_31 ^= phase_mask & 1u;
261 dst[0] = bmc_12_15 | (BMC_ZERO_NIBBLE << 8) | (BMC_ZERO_NIBBLE << 16) | (static_cast<uint32_t>(preamble) << 24);
262 dst[1] = bmc_24_31 | (bmc_16_23 << 16);
265 uint32_t byte_lo = (raw_subframe >> 4) & 0xFF;
266 uint32_t lut_l = BMC_LUT_8[byte_lo];
267 uint32_t bmc_4_11 = lut_l & 0xFFFFu;
270 uint32_t nibble = (raw_subframe >> 12) & 0xF;
272 uint32_t bmc_12_15 = (lut_n & 0xFFu) ^ (phase_mask & 0xFFu);
273 phase_mask ^= lut_n >> 16;
275 uint32_t byte_mid = (raw_subframe >> 16) & 0xFF;
276 uint32_t lut_m = BMC_LUT_8[byte_mid];
277 uint32_t bmc_16_23 = (lut_m & 0xFFFFu) ^ phase_mask;
278 phase_mask ^= lut_m >> 16;
280 uint32_t byte_hi = (raw_subframe >> 24) & 0xFF;
281 uint32_t lut_h = BMC_LUT_8[byte_hi];
282 uint32_t bmc_24_31 = (lut_h & 0xFFFFu) ^ phase_mask;
283 phase_mask ^= lut_h >> 16;
284 bmc_24_31 ^= phase_mask & 1u;
288 dst[0] = bmc_12_15 | (bmc_4_11 << 8) | (static_cast<uint32_t>(preamble) << 24);
289 dst[1] = bmc_24_31 | (bmc_16_23 << 16);
294 static constexpr uint8_t SILENCE[4] = {0, 0, 0, 0};
296 uint8_t preamble_l = (this->
frame_in_block_ == 0) ? PREAMBLE_B : PREAMBLE_M;
318 if (callback ==
nullptr) {
319 return ESP_ERR_INVALID_STATE;
322 esp_err_t err = callback(ctx, this->
spdif_block_buf_.get(), SPDIF_BLOCK_SIZE_BYTES, ticks_to_wait);
334 uint32_t *blocks_sent,
size_t *bytes_consumed) {
335 const uint8_t *pcm_data = src;
336 const uint8_t *
const pcm_end = src +
size;
344 uint32_t *
const block_end = block_buf + SPDIF_BLOCK_SIZE_U32;
348 auto save_state = [&]() {
353 auto report_out_params = [&]() {
354 if (blocks_sent !=
nullptr)
355 *blocks_sent = block_count;
356 if (bytes_consumed !=
nullptr)
357 *bytes_consumed = pcm_data - src;
363 auto maybe_send = [&]() -> esp_err_t {
364 if (block_ptr >= block_end) {
371 block_ptr = block_buf;
382 while (pcm_data + 2 * Bps <= pcm_end) {
384 esp_err_t err = maybe_send();
388 uint32_t c_bit = c_bit_for_frame(channel_status, 0);
389 uint32_t raw_l = build_raw_subframe<Bps>(pcm_data) | c_bit;
390 uint32_t raw_r = build_raw_subframe<Bps>(pcm_data + Bps) | c_bit;
391 bmc_encode_subframe<Bps>(raw_l, PREAMBLE_B, block_ptr);
392 bmc_encode_subframe<Bps>(raw_r, PREAMBLE_W, block_ptr + 2);
401 uint32_t input_frames =
static_cast<uint32_t>(pcm_end - pcm_data) / (2u * Bps);
402 uint32_t end_frame = SPDIF_BLOCK_SAMPLES;
403 if (frame + input_frames < end_frame)
404 end_frame = frame + input_frames;
406 while (frame < end_frame) {
407 uint32_t c_bit = c_bit_for_frame(channel_status, frame);
408 uint32_t raw_l = build_raw_subframe<Bps>(pcm_data) | c_bit;
409 uint32_t raw_r = build_raw_subframe<Bps>(pcm_data + Bps) | c_bit;
410 bmc_encode_subframe<Bps>(raw_l, PREAMBLE_M, block_ptr);
411 bmc_encode_subframe<Bps>(raw_r, PREAMBLE_W, block_ptr + 2);
416 if (frame >= SPDIF_BLOCK_SAMPLES)
421 if (block_ptr >= block_end) {
428 block_ptr = block_buf;
438 size_t *bytes_consumed) {
451 return ESP_ERR_INVALID_STATE;
486 return ESP_ERR_INVALID_STATE;
uint32_t * spdif_block_ptr_
std::unique_ptr< uint32_t[]> spdif_block_buf_
esp_err_t send_block_(TickType_t ticks_to_wait)
Send the completed block via the appropriate callback.
void set_bytes_per_sample(uint8_t bytes_per_sample)
Set input PCM width: 2 = 16-bit, 3 = 24-bit, 4 = 32-bit (truncated to 24-bit on the wire).
std::array< uint8_t, 24 > channel_status_
void * preload_callback_ctx_
esp_err_t write(const uint8_t *src, size_t size, TickType_t ticks_to_wait, uint32_t *blocks_sent=nullptr, size_t *bytes_consumed=nullptr)
Convert PCM audio data to SPDIF BMC encoded data.
void set_sample_rate(uint32_t sample_rate)
Set the sample rate for Channel Status Block encoding.
void encode_silence_frame_()
Encode a single stereo silence frame at the current block position.
uint8_t bytes_per_sample_
bool block_buf_is_silence_block_
SPDIFBlockCallback write_callback_
esp_err_t flush_with_silence_typed_(TickType_t ticks_to_wait)
Templated flush-with-silence.
SPDIFBlockCallback preload_callback_
void reset()
Reset the SPDIF block buffer and position tracking, discarding any partial block.
void build_channel_status_()
Build the channel status block from current configuration.
HOT esp_err_t write_typed_(const uint8_t *src, size_t size, TickType_t ticks_to_wait, uint32_t *blocks_sent, size_t *bytes_consumed)
Templated write loop. Called from the public write() via runtime dispatch on bytes_per_sample_.
esp_err_t flush_with_silence(TickType_t ticks_to_wait)
Emit one complete SPDIF block: pad any pending partial block with silence and send,...
bool setup()
Initialize the SPDIF working buffer.
void * write_callback_ctx_
esp_err_t(*)(void *user_ctx, uint32_t *data, size_t size, TickType_t ticks_to_wait) SPDIFBlockCallback
Callback signature for block completion (raw function pointer for minimal overhead)
static float float float t
const std::vector< uint8_t > & data