Re-enable the parts of the audio pipeline that are working

custom
jacqueline 2 years ago
parent c7901ae429
commit 8ed3d7e31f
  1. 2
      src/audio/audio_playback.cpp
  2. 9
      src/audio/fatfs_audio_input.cpp
  3. 83
      src/drivers/dac.cpp
  4. 18
      src/drivers/include/dac.hpp
  5. 5
      src/main/main.cpp

@ -43,9 +43,11 @@ auto AudioPlayback::create(drivers::GpioExpander* expander,
playback->ConnectElements(codec.get(), sink.get()); playback->ConnectElements(codec.get(), sink.get());
// Launch! // Launch!
/*
playback->element_handles_.push_back(StartAudioTask("src", source)); playback->element_handles_.push_back(StartAudioTask("src", source));
playback->element_handles_.push_back(StartAudioTask("dec", codec)); playback->element_handles_.push_back(StartAudioTask("dec", codec));
playback->element_handles_.push_back(StartAudioTask("sink", sink)); playback->element_handles_.push_back(StartAudioTask("sink", sink));
*/
return playback; return playback;
} }

@ -106,6 +106,8 @@ auto FatfsAudioInput::ProcessIdle() -> cpp::result<void, AudioProcessingError> {
read_size = file_buffer_.begin() - file_buffer_write_pos_; read_size = file_buffer_.begin() - file_buffer_write_pos_;
} }
ESP_LOGI(kTag, "reading up to %d bytes", (int) read_size);
UINT bytes_read = 0; UINT bytes_read = 0;
FRESULT result = FRESULT result =
f_read(&current_file_, std::addressof(file_buffer_write_pos_), f_read(&current_file_, std::addressof(file_buffer_write_pos_),
@ -115,6 +117,8 @@ auto FatfsAudioInput::ProcessIdle() -> cpp::result<void, AudioProcessingError> {
return cpp::fail(IO_ERROR); return cpp::fail(IO_ERROR);
} }
ESP_LOGI(kTag, "actual read size %d bytes", (int) bytes_read);
if (f_eof(&current_file_)) { if (f_eof(&current_file_)) {
f_close(&current_file_); f_close(&current_file_);
is_file_open_ = false; is_file_open_ = false;
@ -130,7 +134,8 @@ auto FatfsAudioInput::ProcessIdle() -> cpp::result<void, AudioProcessingError> {
} }
// Now stream data into the output buffer until it's full. // Now stream data into the output buffer until it's full.
while (1) { while (GetRingBufferDistance() > 0) {
ESP_LOGI(kTag, "writing up to %d bytes", (int) GetRingBufferDistance());
ChunkWriteResult result = chunk_writer_->WriteChunkToStream( ChunkWriteResult result = chunk_writer_->WriteChunkToStream(
[&](cpp::span<std::byte> d) { return SendChunk(d); }, kServiceInterval); [&](cpp::span<std::byte> d) { return SendChunk(d); }, kServiceInterval);
@ -146,6 +151,8 @@ auto FatfsAudioInput::ProcessIdle() -> cpp::result<void, AudioProcessingError> {
return cpp::fail(IO_ERROR); return cpp::fail(IO_ERROR);
} }
} }
return {};
} }
auto FatfsAudioInput::SendChunk(cpp::span<std::byte> dest) -> size_t { auto FatfsAudioInput::SendChunk(cpp::span<std::byte> dest) -> size_t {

@ -4,9 +4,9 @@
#include "assert.h" #include "assert.h"
#include "driver/i2c.h" #include "driver/i2c.h"
#include "driver/i2s.h" #include "driver/i2s_common.h"
#include "driver/i2s_std.h"
#include "driver/i2s_types.h" #include "driver/i2s_types.h"
#include "driver/i2s_types_legacy.h"
#include "esp_err.h" #include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "hal/i2c_types.h" #include "hal/i2c_types.h"
@ -28,45 +28,43 @@ static const AudioDac::BitsPerSample kDefaultBps = AudioDac::BPS_16;
auto AudioDac::create(GpioExpander* expander) auto AudioDac::create(GpioExpander* expander)
-> cpp::result<std::unique_ptr<AudioDac>, Error> { -> cpp::result<std::unique_ptr<AudioDac>, Error> {
// TODO: tune.
i2s_chan_handle_t i2s_handle;
i2s_chan_config_t channel_config = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
i2s_new_channel(&channel_config, &i2s_handle, NULL);
//
// First, instantiate the instance so it can do all of its power on // First, instantiate the instance so it can do all of its power on
// configuration. // configuration.
std::unique_ptr<AudioDac> dac = std::make_unique<AudioDac>(expander); std::unique_ptr<AudioDac> dac = std::make_unique<AudioDac>(expander, i2s_handle);
// Whilst we wait for the initial boot, we can work on installing the I2S // Whilst we wait for the initial boot, we can work on installing the I2S
// driver. // driver.
i2s_config_t i2s_config = { i2s_std_config_t i2s_config = {
// static_cast bc esp-adf uses enums incorrectly .clk_cfg = dac->clock_config_,
.mode = static_cast<i2s_mode_t>(I2S_MODE_MASTER | I2S_MODE_TX), .slot_cfg = dac->slot_config_,
.sample_rate = 44100, .gpio_cfg = {
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .mclk = GPIO_NUM_0,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .bclk = GPIO_NUM_26,
.communication_format = I2S_COMM_FORMAT_STAND_I2S, .ws = GPIO_NUM_27,
.intr_alloc_flags = ESP_INTR_FLAG_LOWMED, .dout = GPIO_NUM_5,
// TODO(jacqueline): tune dma buffer size. this seems very smol. .din = I2S_GPIO_UNUSED,
.dma_buf_count = 8, .invert_flags = {
.dma_buf_len = 64, .mclk_inv = false,
.use_apll = false, .bclk_inv = false,
.tx_desc_auto_clear = false, .ws_inv = false,
.fixed_mclk = 0, }
.mclk_multiple = I2S_MCLK_MULTIPLE_512, // TODO: double check },
.bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
}; };
if (esp_err_t err = if (esp_err_t err = i2s_channel_init_std_mode(i2s_handle, &i2s_config) != ESP_OK) {
i2s_driver_install(kI2SPort, &i2s_config, 0, NULL) != ESP_OK) { ESP_LOGE(kTag, "failed to initialise i2s channel %x", err);
ESP_LOGE(kTag, "failed to configure i2s pins %x", err);
return cpp::fail(Error::FAILED_TO_INSTALL_I2S); return cpp::fail(Error::FAILED_TO_INSTALL_I2S);
} }
i2s_pin_config_t pin_config = {.mck_io_num = GPIO_NUM_0, // TODO: does starting the channel mean the dac will boot into a more
.bck_io_num = GPIO_NUM_26, // meaningful state?
.ws_io_num = GPIO_NUM_27, i2s_channel_enable(dac->i2s_handle_);
.data_out_num = GPIO_NUM_5,
.data_in_num = I2S_PIN_NO_CHANGE};
if (esp_err_t err = i2s_set_pin(kI2SPort, &pin_config) != ESP_OK) {
ESP_LOGE(kTag, "failed to configure i2s pins %x", err);
return cpp::fail(Error::FAILED_TO_INSTALL_I2S);
}
// Now let's double check that the DAC itself came up whilst we we working. // Now let's double check that the DAC itself came up whilst we we working.
bool is_booted = dac->WaitForPowerState( bool is_booted = dac->WaitForPowerState(
@ -92,13 +90,17 @@ auto AudioDac::create(GpioExpander* expander)
return dac; return dac;
} }
AudioDac::AudioDac(GpioExpander* gpio) : gpio_(gpio) { AudioDac::AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle) : gpio_(gpio),
i2s_handle_(i2s_handle),
clock_config_(I2S_STD_CLK_DEFAULT_CONFIG(48000)),
slot_config_(I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO)) {
gpio_->set_pin(GpioExpander::AUDIO_POWER_ENABLE, true); gpio_->set_pin(GpioExpander::AUDIO_POWER_ENABLE, true);
gpio_->Write(); gpio_->Write();
} }
AudioDac::~AudioDac() { AudioDac::~AudioDac() {
i2s_driver_uninstall(kI2SPort); i2s_channel_disable(i2s_handle_);
i2s_del_channel(i2s_handle_);
gpio_->set_pin(GpioExpander::AUDIO_POWER_ENABLE, false); gpio_->set_pin(GpioExpander::AUDIO_POWER_ENABLE, false);
gpio_->Write(); gpio_->Write();
} }
@ -136,7 +138,7 @@ bool AudioDac::WaitForPowerState(
if (has_matched) { if (has_matched) {
break; break;
} else { } else {
ESP_LOGI(kTag, "Waiting for power state (was %d %x)", result.first, ESP_LOGI(kTag, "Waiting for power state (was %d 0x%x)", result.first,
(uint8_t)result.second); (uint8_t)result.second);
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
} }
@ -148,14 +150,23 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> bool {
// TODO(jacqueline): investigate how reliable the auto-clocking of the dac // TODO(jacqueline): investigate how reliable the auto-clocking of the dac
// is. We might need to explicit reconfigure the dac here as well if it's not // is. We might need to explicit reconfigure the dac here as well if it's not
// good enough. // good enough.
i2s_set_clk(kI2SPort, rate, bps, I2S_CHANNEL_STEREO); i2s_channel_disable(i2s_handle_);
slot_config_.slot_bit_width = (i2s_slot_bit_width_t) bps;
i2s_channel_reconfig_std_slot(i2s_handle_, &slot_config_);
// TODO: update mclk multiple as well if needed?
clock_config_.sample_rate_hz = rate;
i2s_channel_reconfig_std_clock(i2s_handle_, &clock_config_);
i2s_channel_enable(i2s_handle_);
return true; return true;
} }
auto AudioDac::WriteData(const cpp::span<std::byte>& data, TickType_t max_wait) auto AudioDac::WriteData(const cpp::span<std::byte>& data, TickType_t max_wait)
-> std::size_t { -> std::size_t {
std::size_t res = 0; std::size_t res = 0;
i2s_write(kI2SPort, data.data(), data.size(), &res, max_wait); i2s_channel_write(i2s_handle_, data.data(), data.size(), &res, max_wait);
return res; return res;
} }

@ -3,13 +3,15 @@
#include <stdint.h> #include <stdint.h>
#include <functional> #include <functional>
#include <memory>
#include <utility>
#include "driver/i2s_types.h"
#include "esp_err.h" #include "esp_err.h"
#include "freertos/portmacro.h" #include "freertos/portmacro.h"
#include "hal/i2s_types.h"
#include "result.hpp" #include "result.hpp"
#include "span.hpp" #include "span.hpp"
#include "driver/i2s_types_legacy.h" #include "driver/i2s_std.h"
#include "gpio_expander.hpp" #include "gpio_expander.hpp"
@ -29,7 +31,7 @@ class AudioDac {
static auto create(GpioExpander* expander) static auto create(GpioExpander* expander)
-> cpp::result<std::unique_ptr<AudioDac>, Error>; -> cpp::result<std::unique_ptr<AudioDac>, Error>;
AudioDac(GpioExpander* gpio); AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle);
~AudioDac(); ~AudioDac();
/** /**
@ -54,9 +56,9 @@ class AudioDac {
std::pair<bool, PowerState> ReadPowerState(); std::pair<bool, PowerState> ReadPowerState();
enum BitsPerSample { enum BitsPerSample {
BPS_16 = I2S_BITS_PER_SAMPLE_16BIT, BPS_16 = I2S_DATA_BIT_WIDTH_16BIT,
BPS_24 = I2S_BITS_PER_SAMPLE_24BIT, BPS_24 = I2S_DATA_BIT_WIDTH_24BIT,
BPS_32 = I2S_BITS_PER_SAMPLE_32BIT BPS_32 = I2S_DATA_BIT_WIDTH_32BIT,
}; };
enum SampleRate { enum SampleRate {
SAMPLE_RATE_44_1 = 44100, SAMPLE_RATE_44_1 = 44100,
@ -75,6 +77,10 @@ class AudioDac {
private: private:
GpioExpander* gpio_; GpioExpander* gpio_;
i2s_chan_handle_t i2s_handle_;
i2s_std_clk_config_t clock_config_;
i2s_std_slot_config_t slot_config_;
/* /*
* Pools the power state for up to 10ms, waiting for the given predicate to * Pools the power state for up to 10ms, waiting for the given predicate to

@ -122,8 +122,6 @@ extern "C" void app_main(void) {
(void*)lvglArgs, 1, sLvglStack, (void*)lvglArgs, 1, sLvglStack,
&sLvglTaskBuffer, 1); &sLvglTaskBuffer, 1);
// TODO(jacqueline): re-enable this once our pipeline works.
/*
ESP_LOGI(TAG, "Init audio pipeline"); ESP_LOGI(TAG, "Init audio pipeline");
auto playback_res = audio::AudioPlayback::create(expander, storage); auto playback_res = audio::AudioPlayback::create(expander, storage);
if (playback_res.has_error()) { if (playback_res.has_error()) {
@ -132,10 +130,9 @@ extern "C" void app_main(void) {
} }
std::shared_ptr<audio::AudioPlayback> playback = std::shared_ptr<audio::AudioPlayback> playback =
std::move(playback_res.value()); std::move(playback_res.value());
*/
ESP_LOGI(TAG, "Launch console"); ESP_LOGI(TAG, "Launch console");
console::AppConsole console(nullptr); console::AppConsole console(playback.get());
console.Launch(); console.Launch();
while (1) { while (1) {

Loading…
Cancel
Save