fix yet more mono i2s issues

custom
jacqueline 1 year ago
parent b6d16a20a4
commit 44e6aee722
  1. 30
      src/drivers/i2s_dac.cpp
  2. 2
      src/drivers/include/i2s_dac.hpp

@ -6,9 +6,6 @@
#include "i2s_dac.hpp"
#include <stdint.h>
#include <sys/_stdint.h>
#include <cmath>
#include <cstdint>
#include <cstring>
@ -50,7 +47,7 @@ auto I2SDac::create(IGpios& expander) -> std::optional<I2SDac*> {
};
ESP_ERROR_CHECK(i2s_new_channel(&channel_config, &i2s_handle, NULL));
//
// First, instantiate the instance so it can do all of its power on
// configuration.
std::unique_ptr<I2SDac> dac = std::make_unique<I2SDac>(expander, i2s_handle);
@ -130,6 +127,8 @@ auto I2SDac::SetPaused(bool paused) -> void {
}
}
static volatile bool sSwapWords = false;
auto I2SDac::Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate)
-> void {
std::lock_guard<std::mutex> lock(configure_mutex_);
@ -146,9 +145,11 @@ auto I2SDac::Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate)
switch (ch) {
case CHANNELS_MONO:
sSwapWords = true;
slot_config_.slot_mode = I2S_SLOT_MODE_MONO;
break;
case CHANNELS_STEREO:
sSwapWords = false;
slot_config_.slot_mode = I2S_SLOT_MODE_STEREO;
break;
}
@ -211,17 +212,32 @@ extern "C" IRAM_ATTR auto callback(i2s_chan_handle_t handle,
if (event->data == nullptr || event->size == 0) {
return false;
}
uint8_t** buf = reinterpret_cast<uint8_t**>(event->data);
assert(event->size % 4 == 0);
uint8_t* buf = *reinterpret_cast<uint8_t**>(event->data);
auto src = reinterpret_cast<StreamBufferHandle_t>(user_ctx);
BaseType_t ret = false;
size_t bytes_written =
xStreamBufferReceiveFromISR(src, *buf, event->size, &ret);
xStreamBufferReceiveFromISR(src, buf, event->size, &ret);
// The ESP32's I2S peripheral has a different endianness to its processors.
// ESP-IDF handles this difference for stereo channels, but not for mono
// channels. We therefore sometimes need to swap each pair of words as they're
// written to the DMA buffer.
if (sSwapWords) {
uint16_t* buf_as_words = reinterpret_cast<uint16_t*>(buf);
for (size_t i = 0; i + 1 < bytes_written / 2; i += 2) {
uint16_t temp = buf_as_words[i];
buf_as_words[i] = buf_as_words[i + 1];
buf_as_words[i + 1] = temp;
}
}
// If we ran out of data, then make sure we clear out the DMA buffers rather
// than continuing to repreat the last few samples.
if (bytes_written < event->size) {
std::memset((*buf) + bytes_written, 0, event->size - bytes_written);
std::memset(buf + bytes_written, 0, event->size - bytes_written);
}
return ret;

@ -32,7 +32,7 @@ namespace drivers {
// means that at 48kHz, we have about 21ms of budget to fill each buffer.
// We base this off of the maximum DMA size in order to minimise the amount of
// work the CPU has to do to service the DMA callbacks.
constexpr size_t kI2SBufferLengthFrames = 1023;
constexpr size_t kI2SBufferLengthFrames = 1024;
/**
* Interface for a DAC that receives PCM samples over I2S.

Loading…
Cancel
Save