diff --git a/src/audio/audio_decoder.cpp b/src/audio/audio_decoder.cpp index 03c7e998..b829f959 100644 --- a/src/audio/audio_decoder.cpp +++ b/src/audio/audio_decoder.cpp @@ -107,9 +107,6 @@ auto AudioDecoder::Process(const std::vector& inputs, } auto write_res = current_codec_->WriteOutputSamples(output->data()); - if (write_res.first > 0) { - ESP_LOGI(kTag, "wrote %u bytes of samples", write_res.first); - } output->add(write_res.first); has_samples_to_send_ = !write_res.second; diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index eb33611b..babe0a97 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -162,6 +162,8 @@ void AudioTaskMain(void* args) { vTaskDelete(NULL); } +static std::byte sDrainBuf[1024]; + void AudioDrainMain(void* args) { { AudioDrainArgs* real_args = reinterpret_cast(args); @@ -171,11 +173,10 @@ void AudioDrainMain(void* args) { // TODO(jacqueline): implement PAUSE without busy-waiting. while (*command != QUIT) { - std::byte buf[64]; - std::size_t len = - xStreamBufferReceive(sink->buffer(), buf, sizeof(buf), portMAX_DELAY); + std::size_t len = xStreamBufferReceive(sink->buffer(), sDrainBuf, + sizeof(sDrainBuf), portMAX_DELAY); if (len > 0) { - sink->Send({buf, len}); + sink->Send({sDrainBuf, len}); } } } diff --git a/src/audio/fatfs_audio_input.cpp b/src/audio/fatfs_audio_input.cpp index 240f7084..b9882711 100644 --- a/src/audio/fatfs_audio_input.cpp +++ b/src/audio/fatfs_audio_input.cpp @@ -65,9 +65,6 @@ auto FatfsAudioInput::Process(const std::vector& inputs, return; } - if (size > 0) { - ESP_LOGI(kTag, "read %u bytes", size); - } output->add(size); if (size < max_size || f_eof(¤t_file_)) { diff --git a/src/codecs/mad.cpp b/src/codecs/mad.cpp index eec2d633..020e0d57 100644 --- a/src/codecs/mad.cpp +++ b/src/codecs/mad.cpp @@ -114,7 +114,7 @@ auto MadMp3Decoder::WriteOutputSamples(cpp::span output) } while (current_sample_ < synth_.pcm.length) { - if (output_byte + (3 * synth_.pcm.channels) >= output.size()) { + if (output_byte + (2 * synth_.pcm.channels) >= output.size()) { return std::make_pair(output_byte, false); } diff --git a/src/drivers/dac.cpp b/src/drivers/dac.cpp index 60679678..40663219 100644 --- a/src/drivers/dac.cpp +++ b/src/drivers/dac.cpp @@ -5,9 +5,7 @@ #include "assert.h" #include "driver/i2c.h" -#include "driver/i2s_common.h" -#include "driver/i2s_std.h" -#include "driver/i2s_types.h" +#include "driver/i2s_types_legacy.h" #include "esp_attr.h" #include "esp_err.h" #include "esp_log.h" @@ -19,6 +17,7 @@ #include "gpio_expander.hpp" #include "hal/i2s_types.h" #include "i2c.hpp" +#include "soc/clk_tree_defs.h" #include "sys/_stdint.h" namespace drivers { @@ -38,6 +37,7 @@ auto AudioDac::create(GpioExpander* expander) i2s_chan_handle_t i2s_handle; i2s_chan_config_t channel_config = I2S_CHANNEL_DEFAULT_CONFIG(kI2SPort, I2S_ROLE_MASTER); + channel_config.auto_clear = true; ESP_ERROR_CHECK(i2s_new_channel(&channel_config, &i2s_handle, NULL)); // @@ -84,20 +84,22 @@ auto AudioDac::create(GpioExpander* expander) // The DAC should be booted but in power down mode, but it might not be if we // didn't shut down cleanly. Reset it to ensure it is in a consistent state. - dac->WriteRegister(Register::POWER_MODE, 1 << 4); - dac->WriteRegister(Register::RESET, 0b10001); + dac->WriteRegister(pcm512x::POWER, 1 << 4); + dac->WriteRegister(pcm512x::RESET, 0b10001); // Use BCK for the internal PLL. // dac->WriteRegister(Register::PLL_CLOCK_SOURCE, 1 << 4); + // dac->WriteRegister(Register::DAC_CLOCK_SOURCE, 0b11 << 5); - // dac->WriteRegister(Register::PLL_ENABLE, 0); - dac->WriteRegister(Register::INTERPOLATION, 1 << 4); + //dac->WriteRegister(Register::PLL_ENABLE, 0); + //dac->WriteRegister(Register::DAC_CLOCK_SOURCE, 0b0110000); + //dac->WriteRegister(Register::CLOCK_ERRORS, 0b01000001); + //dac->WriteRegister(Register::I2S_FORMAT, 0b110000); + // dac->WriteRegister(Register::INTERPOLATION, 1 << 4); dac->Reconfigure(BPS_16, SAMPLE_RATE_44_1); - dac->WriteRegister(Register::POWER_MODE, 0); // Now configure the DAC for standard auto-clock SCK mode. - // dac->WriteRegister(Register::DAC_CLOCK_SOURCE, 0b11 << 5); // Enable auto clocking, and do your best to carry on despite errors. // dac->WriteRegister(Register::CLOCK_ERRORS, 0b1111101); @@ -115,9 +117,11 @@ AudioDac::AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle) : gpio_(gpio), i2s_handle_(i2s_handle), i2s_active_(false), + active_page_(), clock_config_(I2S_STD_CLK_DEFAULT_CONFIG(44100)), - slot_config_(I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, - I2S_SLOT_MODE_STEREO)) { + slot_config_(I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, + I2S_SLOT_MODE_STEREO)) { + clock_config_.clk_src = I2S_CLK_SRC_PLL_160M; gpio_->set_pin(GpioExpander::AMP_EN, true); gpio_->Write(); } @@ -130,29 +134,14 @@ AudioDac::~AudioDac() { } void AudioDac::WriteVolume(uint8_t volume) { - WriteRegister(Register::DIGITAL_VOLUME_L, volume); - WriteRegister(Register::DIGITAL_VOLUME_R, volume); + // Left channel. + WriteRegister(pcm512x::DIGITAL_VOLUME_2, volume); + // Right channel. + WriteRegister(pcm512x::DIGITAL_VOLUME_3, volume); } std::pair AudioDac::ReadPowerState() { - uint8_t result = 0; - - I2CTransaction transaction; - transaction.start() - .write_addr(kPcm5122Address, I2C_MASTER_WRITE) - .write_ack(DSP_BOOT_POWER_STATE) - .start() - .write_addr(kPcm5122Address, I2C_MASTER_READ) - .read(&result, I2C_MASTER_NACK) - .stop(); - - esp_err_t err = transaction.Execute(); - if (err == ESP_ERR_TIMEOUT) { - return std::pair(false, POWERDOWN); - } else { - } - ESP_ERROR_CHECK(err); - + uint8_t result = ReadRegister(pcm512x::POWER_STATE); bool is_booted = result >> 7; PowerState detail = (PowerState)(result & 0b1111); return std::pair(is_booted, detail); @@ -176,17 +165,17 @@ bool AudioDac::WaitForPowerState( } auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void { - WriteRegister(Register::RESYNC_REQUEST, 1); if (i2s_active_) { + WriteRegister(pcm512x::POWER, 1 << 4); i2s_channel_disable(i2s_handle_); } // I2S reconfiguration. - slot_config_.slot_bit_width = (i2s_slot_bit_width_t)bps; + slot_config_.slot_bit_width = I2S_SLOT_BIT_WIDTH_16BIT; ESP_ERROR_CHECK(i2s_channel_reconfig_std_slot(i2s_handle_, &slot_config_)); - clock_config_.sample_rate_hz = rate; + clock_config_.sample_rate_hz = 44100; // If we have an MCLK/SCK, then it must be a multiple of both the sample rate // and the bit clock. At 24 BPS, we therefore have to change the MCLK multiple // to avoid issues at some sample rates. (e.g. 48KHz) @@ -194,21 +183,19 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void { bps == BPS_24 ? I2S_MCLK_MULTIPLE_384 : I2S_MCLK_MULTIPLE_256; ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(i2s_handle_, &clock_config_)); - // DAC reconfiguration. - if (rate == SAMPLE_RATE_44_1) { - WriteRegister(Register::DE_EMPHASIS, 1 << 4); - } else { - WriteRegister(Register::DE_EMPHASIS, 0); - } - // TODO: base on BPS - WriteRegister(Register::I2S_FORMAT, 0b00); + // WriteRegister(Register::I2S_FORMAT, 0b110000); // Configuration is all done, so we can now bring the DAC and I2S stream back // up. I2S first, since otherwise the DAC will see that there's no clocks and // shut itself down. - WriteRegister(Register::RESYNC_REQUEST, 0); ESP_ERROR_CHECK(i2s_channel_enable(i2s_handle_)); + WriteRegister(pcm512x::POWER, 0); + WriteRegister(pcm512x::SYNCHRONIZE, 1); + vTaskDelay(pdMS_TO_TICKS(10)); + WriteRegister(pcm512x::SYNCHRONIZE, 0); + vTaskDelay(pdMS_TO_TICKS(10)); + LogStatus(); i2s_active_ = true; } @@ -223,7 +210,7 @@ auto AudioDac::WriteData(const cpp::span& data) -> void { auto AudioDac::Stop() -> void { LogStatus(); - WriteRegister(Register::POWER_MODE, 1 << 4); + WriteRegister(pcm512x::POWER, 1 << 4); i2s_channel_disable(i2s_handle_); } @@ -237,35 +224,47 @@ auto AudioDac::Stop() -> void { auto AudioDac::LogStatus() -> void { uint8_t res; - res = ReadRegister(Register::SAMPLE_RATE_DETECTION); - ESP_LOGI(kTag, "detected sample rate (want 3): %u", (res >> 4) && 0b111); + res = ReadRegister(pcm512x::RATE_DET_1); + ESP_LOGI(kTag, "detected sample rate (want 3): %u", (res & 0b01110000) >> 4); ESP_LOGI(kTag, "detected SCK ratio (want 6): %u", res && 0b1111); - res = ReadRegister(Register::BCK_DETECTION); + res = ReadRegister(pcm512x::RATE_DET_3); ESP_LOGI(kTag, "detected BCK (want... 16? 32?): %u", res); - res = ReadRegister(Register::CLOCK_ERROR_STATE); + res = ReadRegister(pcm512x::RATE_DET_4); ESP_LOGI(kTag, "clock errors (want zeroes): "); ESP_LOGI(kTag, BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(res & 0b1111111)); - res = ReadRegister(Register::CLOCK_STATUS); + res = ReadRegister(pcm512x::CLOCK_STATUS); ESP_LOGI(kTag, "clock status (want zeroes): "); ESP_LOGI(kTag, BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(res & 0b10111)); - res = ReadRegister(Register::AUTO_MUTE_STATE); - ESP_LOGI(kTag, "automute status (want 3): %u", res & 0b11); - - res = ReadRegister(Register::SOFT_MUTE_STATE); - ESP_LOGI(kTag, "soft mute pin status (want 3): %u", res & 0b11); - - res = ReadRegister(Register::SAMPLE_RATE_STATE); - ESP_LOGI(kTag, "detected sample speed mode (want 0): %u", res & 0b11); + res = ReadRegister(pcm512x::DIGITAL_MUTE_DET); + ESP_LOGI(kTag, "automute status (want 0): %u", res & 0b10001); auto power = ReadPowerState(); ESP_LOGI(kTag, "current power state (want 5): %u", power.second); } -void AudioDac::WriteRegister(Register reg, uint8_t val) { +void AudioDac::WriteRegister(pcm512x::Register r, uint8_t val) { + SelectPage(r.page); + WriteRegisterRaw(r.reg, val); +} + +uint8_t AudioDac::ReadRegister(pcm512x::Register r) { + SelectPage(r.page); + return ReadRegisterRaw(r.reg); +} + +void AudioDac::SelectPage(uint8_t page) { + if (active_page_ && active_page_ == page) { + return; + } + WriteRegisterRaw(0, page); + active_page_ = page; +} + +void AudioDac::WriteRegisterRaw(uint8_t reg, uint8_t val) { I2CTransaction transaction; transaction.start() .write_addr(kPcm5122Address, I2C_MASTER_WRITE) @@ -275,7 +274,7 @@ void AudioDac::WriteRegister(Register reg, uint8_t val) { transaction.Execute(); } -uint8_t AudioDac::ReadRegister(Register reg) { +uint8_t AudioDac::ReadRegisterRaw(uint8_t reg) { uint8_t result = 0; I2CTransaction transaction; transaction.start() diff --git a/src/drivers/include/dac.hpp b/src/drivers/include/dac.hpp index 6849d92c..6836cf59 100644 --- a/src/drivers/include/dac.hpp +++ b/src/drivers/include/dac.hpp @@ -20,6 +20,91 @@ namespace drivers { +namespace pcm512x { +class Register { + public: + uint8_t page; + uint8_t reg; + + constexpr Register(uint8_t page, uint8_t reg) : page(page), reg(reg) {} +}; + +constexpr Register RESET(0, 1); +constexpr Register POWER(0, 2); +constexpr Register MUTE(0, 3); +constexpr Register PLL_EN(0, 4); +constexpr Register SPI_MISO_FUNCTION(0, 6); +constexpr Register DSP(0, 7); +constexpr Register GPIO_EN(0, 8); +constexpr Register BCLK_LRCLK_CFG(0, 9); +constexpr Register DSP_GPIO_INPUT(0, 10); +constexpr Register MASTER_MODE(0, 12); +constexpr Register PLL_REF(0, 13); +constexpr Register DAC_REF(0, 14); +constexpr Register GPIO_DACIN(0, 16); +constexpr Register GPIO_PLLIN(0, 18); +constexpr Register SYNCHRONIZE(0, 19); +constexpr Register PLL_COEFF_0(0, 20); +constexpr Register PLL_COEFF_1(0, 21); +constexpr Register PLL_COEFF_2(0, 22); +constexpr Register PLL_COEFF_3(0, 23); +constexpr Register PLL_COEFF_4(0, 24); +constexpr Register DSP_CLKDIV(0, 27); +constexpr Register DAC_CLKDIV(0, 28); +constexpr Register NCP_CLKDIV(0, 29); +constexpr Register OSR_CLKDIV(0, 30); +constexpr Register MASTER_CLKDIV_1(0, 32); +constexpr Register MASTER_CLKDIV_2(0, 33); +constexpr Register FS_SPEED_MODE(0, 34); +constexpr Register IDAC_1(0, 35); +constexpr Register IDAC_2(0, 36); +constexpr Register ERROR_DETECT(0, 37); +constexpr Register I2S_1(0, 40); +constexpr Register I2S_2(0, 41); +constexpr Register DAC_ROUTING(0, 42); +constexpr Register DSP_PROGRAM(0, 43); +constexpr Register CLKDET(0, 44); +constexpr Register AUTO_MUTE(0, 59); +constexpr Register DIGITAL_VOLUME_1(0, 60); +constexpr Register DIGITAL_VOLUME_2(0, 61); +constexpr Register DIGITAL_VOLUME_3(0, 62); +constexpr Register DIGITAL_MUTE_1(0, 63); +constexpr Register DIGITAL_MUTE_2(0, 64); +constexpr Register DIGITAL_MUTE_3(0, 65); +constexpr Register GPIO_OUTPUT_1(0, 80); +constexpr Register GPIO_OUTPUT_2(0, 81); +constexpr Register GPIO_OUTPUT_3(0, 82); +constexpr Register GPIO_OUTPUT_4(0, 83); +constexpr Register GPIO_OUTPUT_5(0, 84); +constexpr Register GPIO_OUTPUT_6(0, 85); +constexpr Register GPIO_CONTROL_1(0, 86); +constexpr Register GPIO_CONTROL_2(0, 87); +constexpr Register OVERFLOW(0, 90); +constexpr Register RATE_DET_1(0, 91); +constexpr Register RATE_DET_2(0, 92); +constexpr Register RATE_DET_3(0, 93); +constexpr Register RATE_DET_4(0, 94); +constexpr Register CLOCK_STATUS(0, 95); +constexpr Register ANALOG_MUTE_DET(0, 108); +constexpr Register POWER_STATE(0, 118); +constexpr Register GPIN(0, 119); +constexpr Register DIGITAL_MUTE_DET(0, 120); + +constexpr Register OUTPUT_AMPLITUDE(1, 1); +constexpr Register ANALOG_GAIN_CTRL(1, 2); +constexpr Register UNDERVOLTAGE_PROT(1, 5); +constexpr Register ANALOG_MUTE_CTRL(1, 6); +constexpr Register ANALOG_GAIN_BOOST(1, 7); +constexpr Register VCOM_CTRL_1(1, 8); +constexpr Register VCOM_CTRL_2(1, 9); + +constexpr Register CRAM_CTRL(44, 1); + +constexpr Register FLEX_A(253, 63); +constexpr Register FLEX_B(253, 64); + +} // namespace pcm512x + /** * Interface for a PCM5122PWR DAC, configured over I2C. */ @@ -84,6 +169,7 @@ class AudioDac { GpioExpander* gpio_; i2s_chan_handle_t i2s_handle_; bool i2s_active_; + std::optional active_page_; i2s_std_clk_config_t clock_config_; i2s_std_slot_config_t slot_config_; @@ -94,33 +180,12 @@ class AudioDac { */ bool WaitForPowerState(std::function predicate); - enum Register { - PAGE_SELECT = 0, - RESET = 1, - POWER_MODE = 2, - PLL_ENABLE = 4, - DE_EMPHASIS = 7, - PLL_CLOCK_SOURCE = 13, - DAC_CLOCK_SOURCE = 14, - RESYNC_REQUEST = 19, - CLOCK_ERRORS = 37, - INTERPOLATION = 34, - I2S_FORMAT = 40, - DIGITAL_VOLUME_L = 61, - DIGITAL_VOLUME_R = 62, - - SAMPLE_RATE_DETECTION = 91, - BCK_DETECTION = 93, - CLOCK_ERROR_STATE = 94, - CLOCK_STATUS = 95, - AUTO_MUTE_STATE = 108, - SOFT_MUTE_STATE = 114, - SAMPLE_RATE_STATE = 115, - DSP_BOOT_POWER_STATE = 118, - }; + void WriteRegister(pcm512x::Register r, uint8_t val); + uint8_t ReadRegister(pcm512x::Register r); - void WriteRegister(Register reg, uint8_t val); - uint8_t ReadRegister(Register reg); + void SelectPage(uint8_t page); + void WriteRegisterRaw(uint8_t reg, uint8_t val); + uint8_t ReadRegisterRaw(uint8_t reg); }; } // namespace drivers