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. 50
      src/drivers/dac.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);
}

@ -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_;

@ -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:

@ -1,4 +1,5 @@
#include "mad.hpp"
#include <stdint.h>
#include <cstdint>
@ -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<std::byte> 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<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 =
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)&0xFF);
}

@ -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,
@ -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,15 +212,19 @@ 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
// 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
int p, j, d, r;
@ -226,8 +232,8 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
// Clock dividers
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.
// P = 1, R = 2, D = 0 for all supported combinations.
// Set J to have PLL clk = 90.3168 MHz
@ -242,9 +248,7 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
ncp = 4;
dosr = 8;
idac = 1024; // DSP clock / sample rate
}
else
{
} else {
// 8kHz and multiples.
// PLL config for a 98.304 MHz PLL clk
if (bps == BPS_24 && bckFreq > 1536000) {
@ -261,9 +265,15 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
// 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;
@ -272,7 +282,6 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
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;
}

Loading…
Cancel
Save