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);
41static constexpr auto BMC_LUT_4 = [] {
42 std::array<uint8_t, 16>
t{};
44 t[i] =
static_cast<uint8_t
>(bmc_lut_encode(i, 4));
50static constexpr auto BMC_LUT_8 = [] {
51 std::array<uint16_t, 256>
t{};
53 t[i] = bmc_lut_encode(i, 8);
61 ESP_LOGE(TAG,
"Buffer allocation failed (%zu bytes)", SPDIF_BLOCK_SIZE_BYTES);
64 ESP_LOGV(TAG,
"Buffer allocated (%zu bytes)", SPDIF_BLOCK_SIZE_BYTES);
83 ESP_LOGD(TAG,
"Sample rate set to %lu Hz", (
unsigned long) sample_rate);
136 ESP_LOGW(TAG,
"Unsupported sample rate %lu Hz, channel status will indicate 'not specified'",
162 uint32_t raw_subframe = (
static_cast<uint32_t>(pcm_sample[1]) << 20) | (
static_cast<uint32_t>(pcm_sample[0]) << 12);
168 raw_subframe |= (1U << 30);
173 uint32_t bits_4_30 = (raw_subframe >> 4) & 0x07FFFFFF;
174 uint32_t ones_count = __builtin_popcount(bits_4_30);
176 raw_subframe |= parity << 31;
188 preamble = PREAMBLE_W;
215 uint32_t nibble = (raw_subframe >> 12) & 0xF;
216 uint32_t bmc_12_15 = BMC_LUT_4[nibble];
223 uint32_t phase_mask = -(__builtin_popcount(nibble) & 1u) & 0xFFFF;
226 uint32_t byte_mid = (raw_subframe >> 16) & 0xFF;
227 uint32_t bmc_16_23 = BMC_LUT_8[byte_mid] ^ phase_mask;
228 phase_mask ^= -(__builtin_popcount(byte_mid) & 1u) & 0xFFFF;
231 uint32_t byte_hi = (raw_subframe >> 24) & 0xFF;
232 uint32_t bmc_24_31 = BMC_LUT_8[byte_hi] ^ phase_mask;
255 bmc_12_15 | (BMC_ZERO_NIBBLE << 8) | (BMC_ZERO_NIBBLE << 16) | (static_cast<uint32_t>(preamble) << 24);
284 if (callback ==
nullptr) {
285 return ESP_ERR_INVALID_STATE;
288 esp_err_t err = callback(ctx, this->
spdif_block_buf_.get(), SPDIF_BLOCK_SIZE_BYTES, ticks_to_wait);
305 size_t pending_samples = pending_uint32s / 2;
306 return pending_samples * 2;
310 size_t *bytes_consumed) {
311 const uint8_t *pcm_data = src;
312 const uint8_t *pcm_end = src +
size;
315 while (pcm_data < pcm_end) {
320 if (blocks_sent !=
nullptr) {
321 *blocks_sent = block_count;
323 if (bytes_consumed !=
nullptr) {
324 *bytes_consumed = pcm_data - src;
340 if (blocks_sent !=
nullptr) {
341 *blocks_sent = block_count;
343 if (bytes_consumed !=
nullptr) {
344 *bytes_consumed = pcm_data - src;
351 if (blocks_sent !=
nullptr) {
352 *blocks_sent = block_count;
354 if (bytes_consumed !=
nullptr) {
355 *bytes_consumed =
size;
365 static const uint8_t SILENCE[2] = {0, 0};
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.
size_t get_pending_pcm_bytes() const
Get the number of PCM bytes currently pending in the partial block buffer.
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.
SPDIFBlockCallback write_callback_
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.
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.
ESPHOME_ALWAYS_INLINE bool get_channel_status_bit_(uint8_t frame) const
Get the channel status bit for a specific frame.
void * write_callback_ctx_
HOT void encode_sample_(const uint8_t *pcm_sample)
Encode a single 16-bit PCM sample into the current block position.
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