diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index 45db0b60..07f49085 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -37,15 +37,14 @@ namespace task { static const char* kTag = "task"; static const std::size_t kStackSize = 24 * 1024; static const std::size_t kDrainStackSize = 1024; -static const uint8_t kAudioCore = 0; auto StartPipeline(Pipeline* pipeline, IAudioSink* sink) -> void { // Newly created task will free this. AudioTaskArgs* args = new AudioTaskArgs{.pipeline = pipeline, .sink = sink}; ESP_LOGI(kTag, "starting audio pipeline task"); - xTaskCreatePinnedToCore(&AudioTaskMain, "pipeline", kStackSize, args, - kTaskPriorityAudioPipeline, NULL, kAudioCore); + xTaskCreate(&AudioTaskMain, "pipeline", kStackSize, args, + kTaskPriorityAudioPipeline, NULL); } auto StartDrain(IAudioSink* sink) -> void { @@ -151,6 +150,13 @@ void AudioTaskMain(void* args) { std::size_t sent = xStreamBufferSend( sink->buffer(), sink_stream.data().data(), sink_stream.data().size_bytes(), pdMS_TO_TICKS(10)); + if (sent > 0) { + ESP_LOGI(kTag, "sunk %u bytes out of %u (%d %%)", sent, + sink_stream.info().bytes_in_stream, + (int)(((float)sent / + (float)sink_stream.info().bytes_in_stream) * + 100)); + } sink_stream.consume(sent); } diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp index eaed04ec..7a535c35 100644 --- a/src/audio/include/audio_sink.hpp +++ b/src/audio/include/audio_sink.hpp @@ -10,7 +10,7 @@ namespace audio { class IAudioSink { private: // TODO: tune. at least about 12KiB seems right for mp3 - static const std::size_t kDrainBufferSize = 24 * 1024; + static const std::size_t kDrainBufferSize = 48 * 1024; uint8_t* buffer_; StaticStreamBuffer_t* metadata_; StreamBufferHandle_t handle_; diff --git a/src/audio/include/pipeline.hpp b/src/audio/include/pipeline.hpp index 2e9247bc..7a658a38 100644 --- a/src/audio/include/pipeline.hpp +++ b/src/audio/include/pipeline.hpp @@ -13,7 +13,7 @@ namespace audio { -static const std::size_t kPipelineBufferSize = 32 * 1024; +static const std::size_t kPipelineBufferSize = 64 * 1024; class Pipeline { public: diff --git a/src/codecs/mad.cpp b/src/codecs/mad.cpp index 7e5b350b..dd839537 100644 --- a/src/codecs/mad.cpp +++ b/src/codecs/mad.cpp @@ -1,4 +1,5 @@ #include "mad.hpp" +#include #include @@ -9,9 +10,9 @@ namespace codecs { -static int scaleTo16Bits(mad_fixed_t sample) { +static uint32_t scaleToBits(mad_fixed_t sample, uint8_t bits) { // Round the bottom bits. - sample += (1L << (MAD_F_FRACBITS - 16)); + sample += (1L << (MAD_F_FRACBITS - bits)); // Clip the leftover bits to within range. if (sample >= MAD_F_ONE) @@ -20,7 +21,7 @@ static int scaleTo16Bits(mad_fixed_t sample) { sample = -MAD_F_ONE; // Quantize. - return sample >> (MAD_F_FRACBITS + 1 - 16); + return sample >> (MAD_F_FRACBITS + 1 - bits); } MadMp3Decoder::MadMp3Decoder() { @@ -119,8 +120,19 @@ auto MadMp3Decoder::WriteOutputSamples(cpp::span output) } for (int channel = 0; channel < synth_.pcm.channels; channel++) { + // TODO(jacqueline): output 24 bit samples when (if?) we have a downmix + // step in the pipeline. + /* + uint32_t sample_24 = + scaleToBits(synth_.pcm.samples[channel][current_sample_], 24); + output[output_byte++] = static_cast((sample_24 >> 16) & 0xFF); + output[output_byte++] = static_cast((sample_24 >> 8) & 0xFF); + output[output_byte++] = static_cast((sample_24)&0xFF); + // 24 bit samples must still be aligned to 32 bits. The LSB is ignored. + output[output_byte++] = static_cast(0); + */ uint16_t sample_16 = - scaleTo16Bits(synth_.pcm.samples[channel][current_sample_]); + scaleToBits(synth_.pcm.samples[channel][current_sample_], 16); output[output_byte++] = static_cast((sample_16 >> 8) & 0xFF); output[output_byte++] = static_cast((sample_16)&0xFF); } diff --git a/src/drivers/dac.cpp b/src/drivers/dac.cpp index 9fdd7b94..ac283600 100644 --- a/src/drivers/dac.cpp +++ b/src/drivers/dac.cpp @@ -38,7 +38,7 @@ auto AudioDac::create(GpioExpander* expander) channel_config.dma_frame_num = 1024; // Triple buffering should be enough to keep samples flowing smoothly. // TODO(jacqueline): verify this with 192kHz 32bps. - channel_config.dma_desc_num = 8; + channel_config.dma_desc_num = 4; // channel_config.auto_clear = true; ESP_ERROR_CHECK(i2s_new_channel(&channel_config, &i2s_handle, NULL)); @@ -53,7 +53,7 @@ auto AudioDac::create(GpioExpander* expander) i2s_std_config_t i2s_config = { .clk_cfg = dac->clock_config_, .slot_cfg = dac->slot_config_, - .gpio_cfg = {.mclk = I2S_GPIO_UNUSED, + .gpio_cfg = {.mclk = GPIO_NUM_0, .bclk = GPIO_NUM_26, .ws = GPIO_NUM_27, .dout = GPIO_NUM_5, @@ -121,7 +121,7 @@ AudioDac::AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle) 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)) { + I2S_SLOT_MODE_STEREO)) { clock_config_.clk_src = I2S_CLK_SRC_PLL_160M; gpio_->set_pin(GpioExpander::AMP_EN, true); gpio_->Write(); @@ -167,6 +167,8 @@ bool AudioDac::WaitForPowerState( auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void { if (i2s_active_) { + WriteRegister(pcm512x::MUTE, 0b10001); + vTaskDelay(1); WriteRegister(pcm512x::POWER, 1 << 4); i2s_channel_disable(i2s_handle_); } @@ -198,10 +200,10 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void { ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(i2s_handle_, &clock_config_)); // DAC reconfiguration. - //See here : https://e2e.ti.com/support/data_converters/audio_converters/f/64/t/428281 - // for a config example + // Inspired heavily by https://github.com/tommag/PCM51xx_Arduino (MIT). - // Check that the bit clock (PLL input) is between 1MHz and 50MHz + // Check that the bit clock (PLL input) is between 1MHz and 50MHz. It always + // should be. uint32_t bckFreq = rate * bps * 2; if (bckFreq < 1000000 || bckFreq > 50000000) { ESP_LOGE(kTag, "bck freq out of range"); @@ -210,43 +212,45 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void { // 24 bits is not supported for 44.1kHz and 48kHz. if ((rate == SAMPLE_RATE_44_1 || rate == SAMPLE_RATE_48) && bps == BPS_24) { - // TODO(jacqueline): implement + // TODO(jacqueline): I think this *can* be implemented, but requires a bunch + // of maths. ESP_LOGE(kTag, "sample rate and bps mismatch"); return; } - //Initialize system clock from the I2S BCK input - WriteRegister(pcm512x::ERROR_DETECT, 0x1A); // Disable clock autoset and ignore SCK detection - WriteRegister(pcm512x::PLL_REF, 0x10); // Set PLL clock source to BCK - WriteRegister(pcm512x::DAC_REF, 0x10); // Set DAC clock source to PLL output + // Initialize system clock from the I2S BCK input + // Disable clock autoset and ignore SCK detection + WriteRegister(pcm512x::ERROR_DETECT, 0x1A); + // Set PLL clock source to BCK + WriteRegister(pcm512x::PLL_REF, 0x10); + // Set DAC clock source to PLL output + WriteRegister(pcm512x::DAC_REF, 0x10); - //PLL configuration + // PLL configuration int p, j, d, r; - //Clock dividers + // Clock dividers int nmac, ndac, ncp, dosr, idac; - if (rate == SAMPLE_RATE_11_025 || rate == SAMPLE_RATE_22_05 || rate == SAMPLE_RATE_44_1) - { - //44.1kHz and derivatives. - //P = 1, R = 2, D = 0 for all supported combinations. - //Set J to have PLL clk = 90.3168 MHz + if (rate == SAMPLE_RATE_11_025 || rate == SAMPLE_RATE_22_05 || + rate == SAMPLE_RATE_44_1) { + // 44.1kHz and derivatives. + // P = 1, R = 2, D = 0 for all supported combinations. + // Set J to have PLL clk = 90.3168 MHz p = 1; r = 2; j = 90316800 / bckFreq / r; d = 0; - //Derive clocks from the 90.3168MHz PLL + // Derive clocks from the 90.3168MHz PLL nmac = 2; ndac = 16; ncp = 4; dosr = 8; - idac = 1024; // DSP clock / sample rate - } - else - { - //8kHz and multiples. - //PLL config for a 98.304 MHz PLL clk + idac = 1024; // DSP clock / sample rate + } else { + // 8kHz and multiples. + // PLL config for a 98.304 MHz PLL clk if (bps == BPS_24 && bckFreq > 1536000) { p = 3; } else if (bckFreq > 12288000) { @@ -259,20 +263,25 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void { j = 98304000 / (bckFreq / p) / r; d = 0; - //Derive clocks from the 98.304MHz PLL + // Derive clocks from the 98.304MHz PLL switch (rate) { - case SAMPLE_RATE_16: nmac = 6; break; - case SAMPLE_RATE_32: nmac = 3; break; - default: nmac = 2; break; + case SAMPLE_RATE_16: + nmac = 6; + break; + case SAMPLE_RATE_32: + nmac = 3; + break; + default: + nmac = 2; + break; } ndac = 16; ncp = 4; dosr = 384000 / rate; - idac = 98304000 / nmac / rate; // DSP clock / sample rate + idac = 98304000 / nmac / rate; // DSP clock / sample rate } - // Configure PLL WriteRegister(pcm512x::PLL_COEFF_0, p - 1); WriteRegister(pcm512x::PLL_COEFF_1, j); @@ -311,6 +320,11 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void { // shut itself down. ESP_ERROR_CHECK(i2s_channel_enable(i2s_handle_)); WriteRegister(pcm512x::POWER, 0); + + if (i2s_active_) { + vTaskDelay(1); + WriteRegister(pcm512x::MUTE, 0); + } i2s_active_ = true; }