Switch to an MVP-ready 16bit three wire DAC setup

custom
jacqueline 2 years ago
parent 2d95b63727
commit 27c63ebb95
  1. 12
      src/audio/audio_task.cpp
  2. 2
      src/audio/include/audio_sink.hpp
  3. 2
      src/audio/include/pipeline.hpp
  4. 20
      src/codecs/mad.cpp
  5. 70
      src/drivers/dac.cpp

@ -37,15 +37,14 @@ namespace task {
static const char* kTag = "task"; static const char* kTag = "task";
static const std::size_t kStackSize = 24 * 1024; static const std::size_t kStackSize = 24 * 1024;
static const std::size_t kDrainStackSize = 1024; static const std::size_t kDrainStackSize = 1024;
static const uint8_t kAudioCore = 0;
auto StartPipeline(Pipeline* pipeline, IAudioSink* sink) -> void { auto StartPipeline(Pipeline* pipeline, IAudioSink* sink) -> void {
// Newly created task will free this. // Newly created task will free this.
AudioTaskArgs* args = new AudioTaskArgs{.pipeline = pipeline, .sink = sink}; AudioTaskArgs* args = new AudioTaskArgs{.pipeline = pipeline, .sink = sink};
ESP_LOGI(kTag, "starting audio pipeline task"); ESP_LOGI(kTag, "starting audio pipeline task");
xTaskCreatePinnedToCore(&AudioTaskMain, "pipeline", kStackSize, args, xTaskCreate(&AudioTaskMain, "pipeline", kStackSize, args,
kTaskPriorityAudioPipeline, NULL, kAudioCore); kTaskPriorityAudioPipeline, NULL);
} }
auto StartDrain(IAudioSink* sink) -> void { auto StartDrain(IAudioSink* sink) -> void {
@ -151,6 +150,13 @@ void AudioTaskMain(void* args) {
std::size_t sent = xStreamBufferSend( std::size_t sent = xStreamBufferSend(
sink->buffer(), sink_stream.data().data(), sink->buffer(), sink_stream.data().data(),
sink_stream.data().size_bytes(), pdMS_TO_TICKS(10)); 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); sink_stream.consume(sent);
} }

@ -10,7 +10,7 @@ namespace audio {
class IAudioSink { class IAudioSink {
private: private:
// TODO: tune. at least about 12KiB seems right for mp3 // 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_; uint8_t* buffer_;
StaticStreamBuffer_t* metadata_; StaticStreamBuffer_t* metadata_;
StreamBufferHandle_t handle_; StreamBufferHandle_t handle_;

@ -13,7 +13,7 @@
namespace audio { namespace audio {
static const std::size_t kPipelineBufferSize = 32 * 1024; static const std::size_t kPipelineBufferSize = 64 * 1024;
class Pipeline { class Pipeline {
public: public:

@ -1,4 +1,5 @@
#include "mad.hpp" #include "mad.hpp"
#include <stdint.h>
#include <cstdint> #include <cstdint>
@ -9,9 +10,9 @@
namespace codecs { namespace codecs {
static int scaleTo16Bits(mad_fixed_t sample) { static uint32_t scaleToBits(mad_fixed_t sample, uint8_t bits) {
// Round the bottom bits. // Round the bottom bits.
sample += (1L << (MAD_F_FRACBITS - 16)); sample += (1L << (MAD_F_FRACBITS - bits));
// Clip the leftover bits to within range. // Clip the leftover bits to within range.
if (sample >= MAD_F_ONE) if (sample >= MAD_F_ONE)
@ -20,7 +21,7 @@ static int scaleTo16Bits(mad_fixed_t sample) {
sample = -MAD_F_ONE; sample = -MAD_F_ONE;
// Quantize. // Quantize.
return sample >> (MAD_F_FRACBITS + 1 - 16); return sample >> (MAD_F_FRACBITS + 1 - bits);
} }
MadMp3Decoder::MadMp3Decoder() { MadMp3Decoder::MadMp3Decoder() {
@ -119,8 +120,19 @@ auto MadMp3Decoder::WriteOutputSamples(cpp::span<std::byte> output)
} }
for (int channel = 0; channel < synth_.pcm.channels; channel++) { 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<std::byte>((sample_24 >> 16) & 0xFF);
output[output_byte++] = static_cast<std::byte>((sample_24 >> 8) & 0xFF);
output[output_byte++] = static_cast<std::byte>((sample_24)&0xFF);
// 24 bit samples must still be aligned to 32 bits. The LSB is ignored.
output[output_byte++] = static_cast<std::byte>(0);
*/
uint16_t sample_16 = uint16_t sample_16 =
scaleTo16Bits(synth_.pcm.samples[channel][current_sample_]); scaleToBits(synth_.pcm.samples[channel][current_sample_], 16);
output[output_byte++] = static_cast<std::byte>((sample_16 >> 8) & 0xFF); output[output_byte++] = static_cast<std::byte>((sample_16 >> 8) & 0xFF);
output[output_byte++] = static_cast<std::byte>((sample_16)&0xFF); output[output_byte++] = static_cast<std::byte>((sample_16)&0xFF);
} }

@ -38,7 +38,7 @@ auto AudioDac::create(GpioExpander* expander)
channel_config.dma_frame_num = 1024; channel_config.dma_frame_num = 1024;
// Triple buffering should be enough to keep samples flowing smoothly. // Triple buffering should be enough to keep samples flowing smoothly.
// TODO(jacqueline): verify this with 192kHz 32bps. // TODO(jacqueline): verify this with 192kHz 32bps.
channel_config.dma_desc_num = 8; channel_config.dma_desc_num = 4;
// channel_config.auto_clear = true; // channel_config.auto_clear = true;
ESP_ERROR_CHECK(i2s_new_channel(&channel_config, &i2s_handle, NULL)); 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 = { i2s_std_config_t i2s_config = {
.clk_cfg = dac->clock_config_, .clk_cfg = dac->clock_config_,
.slot_cfg = dac->slot_config_, .slot_cfg = dac->slot_config_,
.gpio_cfg = {.mclk = I2S_GPIO_UNUSED, .gpio_cfg = {.mclk = GPIO_NUM_0,
.bclk = GPIO_NUM_26, .bclk = GPIO_NUM_26,
.ws = GPIO_NUM_27, .ws = GPIO_NUM_27,
.dout = GPIO_NUM_5, .dout = GPIO_NUM_5,
@ -167,6 +167,8 @@ bool AudioDac::WaitForPowerState(
auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void { auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
if (i2s_active_) { if (i2s_active_) {
WriteRegister(pcm512x::MUTE, 0b10001);
vTaskDelay(1);
WriteRegister(pcm512x::POWER, 1 << 4); WriteRegister(pcm512x::POWER, 1 << 4);
i2s_channel_disable(i2s_handle_); 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_)); ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(i2s_handle_, &clock_config_));
// DAC reconfiguration. // DAC reconfiguration.
//See here : https://e2e.ti.com/support/data_converters/audio_converters/f/64/t/428281 // Inspired heavily by https://github.com/tommag/PCM51xx_Arduino (MIT).
// for a config example
// 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; uint32_t bckFreq = rate * bps * 2;
if (bckFreq < 1000000 || bckFreq > 50000000) { if (bckFreq < 1000000 || bckFreq > 50000000) {
ESP_LOGE(kTag, "bck freq out of range"); 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. // 24 bits is not supported for 44.1kHz and 48kHz.
if ((rate == SAMPLE_RATE_44_1 || rate == SAMPLE_RATE_48) && bps == BPS_24) { 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"); ESP_LOGE(kTag, "sample rate and bps mismatch");
return; return;
} }
//Initialize system clock from the I2S BCK input // Initialize system clock from the I2S BCK input
WriteRegister(pcm512x::ERROR_DETECT, 0x1A); // Disable clock autoset and ignore SCK detection // Disable clock autoset and ignore SCK detection
WriteRegister(pcm512x::PLL_REF, 0x10); // Set PLL clock source to BCK WriteRegister(pcm512x::ERROR_DETECT, 0x1A);
WriteRegister(pcm512x::DAC_REF, 0x10); // Set DAC clock source to PLL output // 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; int p, j, d, r;
//Clock dividers // Clock dividers
int nmac, ndac, ncp, dosr, idac; int nmac, ndac, ncp, dosr, idac;
if (rate == SAMPLE_RATE_11_025 || rate == SAMPLE_RATE_22_05 || rate == SAMPLE_RATE_44_1) if (rate == SAMPLE_RATE_11_025 || rate == SAMPLE_RATE_22_05 ||
{ rate == SAMPLE_RATE_44_1) {
//44.1kHz and derivatives. // 44.1kHz and derivatives.
//P = 1, R = 2, D = 0 for all supported combinations. // P = 1, R = 2, D = 0 for all supported combinations.
//Set J to have PLL clk = 90.3168 MHz // Set J to have PLL clk = 90.3168 MHz
p = 1; p = 1;
r = 2; r = 2;
j = 90316800 / bckFreq / r; j = 90316800 / bckFreq / r;
d = 0; d = 0;
//Derive clocks from the 90.3168MHz PLL // Derive clocks from the 90.3168MHz PLL
nmac = 2; nmac = 2;
ndac = 16; ndac = 16;
ncp = 4; ncp = 4;
dosr = 8; dosr = 8;
idac = 1024; // DSP clock / sample rate idac = 1024; // DSP clock / sample rate
} } else {
else // 8kHz and multiples.
{ // PLL config for a 98.304 MHz PLL clk
//8kHz and multiples.
//PLL config for a 98.304 MHz PLL clk
if (bps == BPS_24 && bckFreq > 1536000) { if (bps == BPS_24 && bckFreq > 1536000) {
p = 3; p = 3;
} else if (bckFreq > 12288000) { } else if (bckFreq > 12288000) {
@ -259,11 +263,17 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
j = 98304000 / (bckFreq / p) / r; j = 98304000 / (bckFreq / p) / r;
d = 0; d = 0;
//Derive clocks from the 98.304MHz PLL // Derive clocks from the 98.304MHz PLL
switch (rate) { switch (rate) {
case SAMPLE_RATE_16: nmac = 6; break; case SAMPLE_RATE_16:
case SAMPLE_RATE_32: nmac = 3; break; nmac = 6;
default: nmac = 2; break; break;
case SAMPLE_RATE_32:
nmac = 3;
break;
default:
nmac = 2;
break;
} }
ndac = 16; ndac = 16;
@ -272,7 +282,6 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
idac = 98304000 / nmac / rate; // DSP clock / sample rate idac = 98304000 / nmac / rate; // DSP clock / sample rate
} }
// Configure PLL // Configure PLL
WriteRegister(pcm512x::PLL_COEFF_0, p - 1); WriteRegister(pcm512x::PLL_COEFF_0, p - 1);
WriteRegister(pcm512x::PLL_COEFF_1, j); WriteRegister(pcm512x::PLL_COEFF_1, j);
@ -311,6 +320,11 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
// shut itself down. // shut itself down.
ESP_ERROR_CHECK(i2s_channel_enable(i2s_handle_)); ESP_ERROR_CHECK(i2s_channel_enable(i2s_handle_));
WriteRegister(pcm512x::POWER, 0); WriteRegister(pcm512x::POWER, 0);
if (i2s_active_) {
vTaskDelay(1);
WriteRegister(pcm512x::MUTE, 0);
}
i2s_active_ = true; i2s_active_ = true;
} }

Loading…
Cancel
Save