summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-01-03 19:33:24 +1100
committerjacqueline <me@jacqueline.id.au>2024-01-03 19:33:24 +1100
commit44e6aee722aedf3b642228f51c8f853b302ae94e (patch)
tree67ab97aa526f509fd1ca6493be0bdf3d061c6250
parentb6d16a20a4fc9e35c931af9064da309c64796689 (diff)
downloadtangara-fw-44e6aee722aedf3b642228f51c8f853b302ae94e.tar.gz
fix yet more mono i2s issues
-rw-r--r--src/drivers/i2s_dac.cpp30
-rw-r--r--src/drivers/include/i2s_dac.hpp2
2 files changed, 24 insertions, 8 deletions
diff --git a/src/drivers/i2s_dac.cpp b/src/drivers/i2s_dac.cpp
index 021be901..0ef1005a 100644
--- a/src/drivers/i2s_dac.cpp
+++ b/src/drivers/i2s_dac.cpp
@@ -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;
diff --git a/src/drivers/include/i2s_dac.hpp b/src/drivers/include/i2s_dac.hpp
index d66d9762..bd837ca0 100644
--- a/src/drivers/include/i2s_dac.hpp
+++ b/src/drivers/include/i2s_dac.hpp
@@ -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.