summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-04-03 14:06:30 +1000
committerjacqueline <me@jacqueline.id.au>2023-04-19 10:29:40 +1000
commit3836768bb8b95188e6657ab69027d1d9e4b13a77 (patch)
treee4330202da1375a6f49d959a7b839643adccd3ca
parent7c6fd654f50e6665efa4226c6b927f9762734182 (diff)
downloadtangara-fw-3836768bb8b95188e6657ab69027d1d9e4b13a77.tar.gz
new pipeline working(?), but the dac eludes me
-rw-r--r--src/audio/audio_decoder.cpp10
-rw-r--r--src/audio/audio_playback.cpp5
-rw-r--r--src/audio/audio_task.cpp20
-rw-r--r--src/audio/fatfs_audio_input.cpp3
-rw-r--r--src/audio/i2s_audio_output.cpp6
-rw-r--r--src/audio/include/audio_sink.hpp1
-rw-r--r--src/audio/include/i2s_audio_output.hpp1
-rw-r--r--src/audio/include/stream_info.hpp13
-rw-r--r--src/audio/pipeline.cpp5
-rw-r--r--src/drivers/dac.cpp66
-rw-r--r--src/drivers/include/dac.hpp5
-rw-r--r--src/main/main.cpp2
-rw-r--r--src/memory/include/himem.hpp13
13 files changed, 112 insertions, 38 deletions
diff --git a/src/audio/audio_decoder.cpp b/src/audio/audio_decoder.cpp
index ada1f8f7..03c7e998 100644
--- a/src/audio/audio_decoder.cpp
+++ b/src/audio/audio_decoder.cpp
@@ -6,6 +6,7 @@
#include <cstddef>
#include <cstdint>
#include <memory>
+#include <variant>
#include "cbor/tinycbor/src/cborinternal_p.h"
#include "freertos/FreeRTOS.h"
@@ -37,6 +38,7 @@ auto AudioDecoder::ProcessStreamInfo(const StreamInfo& info) -> bool {
if (!std::holds_alternative<StreamInfo::Encoded>(info.format)) {
return false;
}
+ ESP_LOGI(kTag, "got new stream");
const auto& encoded = std::get<StreamInfo::Encoded>(info.format);
// Reuse the existing codec if we can. This will help with gapless playback,
@@ -45,12 +47,14 @@ auto AudioDecoder::ProcessStreamInfo(const StreamInfo& info) -> bool {
if (current_codec_ != nullptr &&
current_codec_->CanHandleType(encoded.type)) {
current_codec_->ResetForNewStream();
+ ESP_LOGI(kTag, "reusing existing decoder");
return true;
}
// TODO: use audio type from stream
auto result = codecs::CreateCodecForType(encoded.type);
if (result.has_value()) {
+ ESP_LOGI(kTag, "creating new decoder");
current_codec_ = std::move(result.value());
} else {
ESP_LOGE(kTag, "no codec for this file");
@@ -73,6 +77,9 @@ auto AudioDecoder::Process(const std::vector<InputStream>& inputs,
}
const StreamInfo& info = input->info();
+ if (std::holds_alternative<std::monostate>(info.format)) {
+ return;
+ }
if (!current_input_format_ || *current_input_format_ != info.format) {
// The input stream has changed! Immediately throw everything away and
// start from scratch.
@@ -100,6 +107,9 @@ auto AudioDecoder::Process(const std::vector<InputStream>& inputs,
}
auto write_res = current_codec_->WriteOutputSamples(output->data());
+ if (write_res.first > 0) {
+ ESP_LOGI(kTag, "wrote %u bytes of samples", write_res.first);
+ }
output->add(write_res.first);
has_samples_to_send_ = !write_res.second;
diff --git a/src/audio/audio_playback.cpp b/src/audio/audio_playback.cpp
index 89139ec4..9a978535 100644
--- a/src/audio/audio_playback.cpp
+++ b/src/audio/audio_playback.cpp
@@ -32,7 +32,8 @@ auto AudioPlayback::create(drivers::GpioExpander* expander)
}
AudioPlayback::AudioPlayback(std::unique_ptr<I2SAudioOutput> output)
- : file_source_(), i2s_output_(std::move(output)) {
+ : file_source_(std::make_unique<FatfsAudioInput>()),
+ i2s_output_(std::move(output)) {
AudioDecoder* codec = new AudioDecoder();
elements_.emplace_back(codec);
@@ -51,7 +52,7 @@ auto AudioPlayback::Play(const std::string& filename) -> void {
}
auto AudioPlayback::LogStatus() -> void {
- // TODO.
+ i2s_output_->Log();
}
} // namespace audio
diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp
index e6c7778c..eb33611b 100644
--- a/src/audio/audio_task.cpp
+++ b/src/audio/audio_task.cpp
@@ -7,6 +7,7 @@
#include <cstdint>
#include <deque>
#include <memory>
+#include <variant>
#include "audio_sink.hpp"
#include "cbor.h"
@@ -114,16 +115,29 @@ void AudioTaskMain(void* args) {
OutputStream out_stream(&raw_out_stream);
elements.at(i)->OutputElement()->Process(in_streams, &out_stream);
+
+ std::for_each(in_regions.begin(), in_regions.end(),
+ [](auto&& r) { r.Unmap(); });
+ out_region.Unmap();
}
- RawStream raw_sink_stream = elements.back()->OutStream(&out_region);
+ RawStream raw_sink_stream = elements.front()->OutStream(&out_region);
InputStream sink_stream(&raw_sink_stream);
- if (!output_format || output_format != sink_stream.info().format) {
+ if (sink_stream.data().size_bytes() == 0) {
+ out_region.Unmap();
+ vTaskDelay(pdMS_TO_TICKS(100));
+ continue;
+ }
+
+ if ((!output_format || output_format != sink_stream.info().format) &&
+ !std::holds_alternative<std::monostate>(
+ sink_stream.info().format)) {
// The format of the stream within the sink stream has changed. We
// need to reconfigure the sink, but shouldn't do so until we've fully
// drained the current buffer.
if (xStreamBufferIsEmpty(sink->buffer())) {
+ ESP_LOGI(kTag, "reconfiguring dac");
output_format = sink_stream.info().format;
sink->Configure(*output_format);
}
@@ -140,6 +154,8 @@ void AudioTaskMain(void* args) {
sink_stream.data().size_bytes(), pdMS_TO_TICKS(10));
sink_stream.consume(sent);
}
+
+ out_region.Unmap();
}
}
}
diff --git a/src/audio/fatfs_audio_input.cpp b/src/audio/fatfs_audio_input.cpp
index b9882711..240f7084 100644
--- a/src/audio/fatfs_audio_input.cpp
+++ b/src/audio/fatfs_audio_input.cpp
@@ -65,6 +65,9 @@ auto FatfsAudioInput::Process(const std::vector<InputStream>& inputs,
return;
}
+ if (size > 0) {
+ ESP_LOGI(kTag, "read %u bytes", size);
+ }
output->add(size);
if (size < max_size || f_eof(&current_file_)) {
diff --git a/src/audio/i2s_audio_output.cpp b/src/audio/i2s_audio_output.cpp
index 55d45001..2d336152 100644
--- a/src/audio/i2s_audio_output.cpp
+++ b/src/audio/i2s_audio_output.cpp
@@ -44,12 +44,14 @@ I2SAudioOutput::~I2SAudioOutput() {}
auto I2SAudioOutput::Configure(const StreamInfo::Format& format) -> bool {
if (!std::holds_alternative<StreamInfo::Pcm>(format)) {
+ ESP_LOGI(kTag, "ignoring non-pcm stream (%d)", format.index());
return false;
}
StreamInfo::Pcm pcm = std::get<StreamInfo::Pcm>(format);
if (current_config_ && pcm == *current_config_) {
+ ESP_LOGI(kTag, "ignoring unchanged format");
return true;
}
@@ -97,6 +99,10 @@ auto I2SAudioOutput::Send(const cpp::span<std::byte>& data) -> void {
dac_->WriteData(data);
}
+auto I2SAudioOutput::Log() -> void {
+ dac_->LogStatus();
+}
+
auto I2SAudioOutput::SetVolume(uint8_t volume) -> void {
dac_->WriteVolume(volume);
}
diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp
index ed7eb02b..ad63ec2e 100644
--- a/src/audio/include/audio_sink.hpp
+++ b/src/audio/include/audio_sink.hpp
@@ -15,6 +15,7 @@ class IAudioSink {
virtual auto Configure(const StreamInfo::Format& format) -> bool = 0;
virtual auto Send(const cpp::span<std::byte>& data) -> void = 0;
+ virtual auto Log() -> void {}
auto buffer() const -> StreamBufferHandle_t { return buffer_; }
};
diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp
index 77019228..31510a91 100644
--- a/src/audio/include/i2s_audio_output.hpp
+++ b/src/audio/include/i2s_audio_output.hpp
@@ -27,6 +27,7 @@ class I2SAudioOutput : public IAudioSink {
auto Configure(const StreamInfo::Format& format) -> bool override;
auto Send(const cpp::span<std::byte>& data) -> void override;
+ auto Log() -> void override;
I2SAudioOutput(const I2SAudioOutput&) = delete;
I2SAudioOutput& operator=(const I2SAudioOutput&) = delete;
diff --git a/src/audio/include/stream_info.hpp b/src/audio/include/stream_info.hpp
index 5622517f..5a36384c 100644
--- a/src/audio/include/stream_info.hpp
+++ b/src/audio/include/stream_info.hpp
@@ -17,12 +17,12 @@ namespace audio {
struct StreamInfo {
// The number of bytes that are available for consumption within this
// stream's buffer.
- std::size_t bytes_in_stream;
+ std::size_t bytes_in_stream{0};
// The total length of this stream, in case its source is finite (e.g. a
// file on disk). May be absent for endless streams (internet streams,
// generated audio, etc.)
- std::optional<std::size_t> length_bytes;
+ std::optional<std::size_t> length_bytes{};
struct Encoded {
// The codec that this stream is associated with.
@@ -42,8 +42,8 @@ struct StreamInfo {
bool operator==(const Pcm&) const = default;
};
- typedef std::variant<Encoded, Pcm> Format;
- Format format;
+ typedef std::variant<std::monostate, Encoded, Pcm> Format;
+ Format format{};
bool operator==(const StreamInfo&) const = default;
};
@@ -91,8 +91,11 @@ class OutputStream {
void add(std::size_t bytes) const { raw_->info->bytes_in_stream += bytes; }
bool prepare(const StreamInfo::Format& new_format) {
- if (new_format == raw_->info->format) {
+ if (std::holds_alternative<std::monostate>(raw_->info->format)) {
raw_->info->format = new_format;
+ raw_->info->bytes_in_stream = 0;
+ }
+ if (new_format == raw_->info->format) {
return true;
}
if (raw_->is_incomplete) {
diff --git a/src/audio/pipeline.cpp b/src/audio/pipeline.cpp
index 8af8f215..bab2f3ff 100644
--- a/src/audio/pipeline.cpp
+++ b/src/audio/pipeline.cpp
@@ -4,7 +4,10 @@
namespace audio {
-Pipeline::Pipeline(IAudioElement* output) : root_(output), subtrees_() {}
+Pipeline::Pipeline(IAudioElement* output) : root_(output), subtrees_() {
+ assert(output != nullptr);
+}
+
Pipeline::~Pipeline() {}
auto Pipeline::AddInput(IAudioElement* input) -> Pipeline* {
diff --git a/src/drivers/dac.cpp b/src/drivers/dac.cpp
index 1f3ba557..60679678 100644
--- a/src/drivers/dac.cpp
+++ b/src/drivers/dac.cpp
@@ -13,6 +13,7 @@
#include "esp_log.h"
#include "freertos/portmacro.h"
#include "freertos/projdefs.h"
+#include "hal/gpio_types.h"
#include "hal/i2c_types.h"
#include "gpio_expander.hpp"
@@ -50,21 +51,23 @@ auto AudioDac::create(GpioExpander* expander)
i2s_std_config_t i2s_config = {
.clk_cfg = dac->clock_config_,
.slot_cfg = dac->slot_config_,
- .gpio_cfg =
- {// TODO: investigate running in three wire mode for less noise
- .mclk = GPIO_NUM_0,
- .bclk = GPIO_NUM_26,
- .ws = GPIO_NUM_27,
- .dout = GPIO_NUM_5,
- .din = I2S_GPIO_UNUSED,
- .invert_flags =
- {
- .mclk_inv = false,
- .bclk_inv = false,
- .ws_inv = false,
- }},
+ .gpio_cfg = {.mclk = GPIO_NUM_0,
+ //.mclk = I2S_GPIO_UNUSED,
+ .bclk = GPIO_NUM_26,
+ .ws = GPIO_NUM_27,
+ .dout = GPIO_NUM_5,
+ .din = I2S_GPIO_UNUSED,
+ .invert_flags =
+ {
+ .mclk_inv = false,
+ .bclk_inv = false,
+ .ws_inv = false,
+ }},
};
+ // gpio_set_direction(GPIO_NUM_0, GPIO_MODE_OUTPUT);
+ // gpio_set_level(GPIO_NUM_0, 0);
+
if (esp_err_t err =
i2s_channel_init_std_mode(i2s_handle, &i2s_config) != ESP_OK) {
ESP_LOGE(kTag, "failed to initialise i2s channel %x", err);
@@ -81,20 +84,29 @@ auto AudioDac::create(GpioExpander* expander)
// The DAC should be booted but in power down mode, but it might not be if we
// didn't shut down cleanly. Reset it to ensure it is in a consistent state.
- dac->WriteRegister(Register::POWER_MODE, 0b10001);
dac->WriteRegister(Register::POWER_MODE, 1 << 4);
dac->WriteRegister(Register::RESET, 0b10001);
+ // Use BCK for the internal PLL.
+ // dac->WriteRegister(Register::PLL_CLOCK_SOURCE, 1 << 4);
+
+ // dac->WriteRegister(Register::PLL_ENABLE, 0);
+ dac->WriteRegister(Register::INTERPOLATION, 1 << 4);
+
+ dac->Reconfigure(BPS_16, SAMPLE_RATE_44_1);
+ dac->WriteRegister(Register::POWER_MODE, 0);
+
// Now configure the DAC for standard auto-clock SCK mode.
- dac->WriteRegister(Register::DAC_CLOCK_SOURCE, 0b11 << 5);
+ // dac->WriteRegister(Register::DAC_CLOCK_SOURCE, 0b11 << 5);
// Enable auto clocking, and do your best to carry on despite errors.
// dac->WriteRegister(Register::CLOCK_ERRORS, 0b1111101);
- i2s_channel_enable(dac->i2s_handle_);
+ // i2s_channel_enable(dac->i2s_handle_);
- dac->WaitForPowerState(
- [](bool booted, PowerState state) { return state == STANDBY; });
+ dac->WaitForPowerState([](bool booted, PowerState state) {
+ return state == RUN || state == STANDBY;
+ });
return dac;
}
@@ -102,6 +114,7 @@ auto AudioDac::create(GpioExpander* expander)
AudioDac::AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle)
: gpio_(gpio),
i2s_handle_(i2s_handle),
+ i2s_active_(false),
clock_config_(I2S_STD_CLK_DEFAULT_CONFIG(44100)),
slot_config_(I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_STEREO)) {
@@ -163,9 +176,10 @@ bool AudioDac::WaitForPowerState(
}
auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
- // Disable the current output, if it isn't already stopped.
- WriteRegister(Register::POWER_MODE, 1 << 4);
- i2s_channel_disable(i2s_handle_);
+ WriteRegister(Register::RESYNC_REQUEST, 1);
+ if (i2s_active_) {
+ i2s_channel_disable(i2s_handle_);
+ }
// I2S reconfiguration.
@@ -181,15 +195,21 @@ auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void {
ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(i2s_handle_, &clock_config_));
// DAC reconfiguration.
+ if (rate == SAMPLE_RATE_44_1) {
+ WriteRegister(Register::DE_EMPHASIS, 1 << 4);
+ } else {
+ WriteRegister(Register::DE_EMPHASIS, 0);
+ }
// TODO: base on BPS
- WriteRegister(Register::I2S_FORMAT, 0);
+ WriteRegister(Register::I2S_FORMAT, 0b00);
// Configuration is all done, so we can now bring the DAC and I2S stream back
// up. I2S first, since otherwise the DAC will see that there's no clocks and
// shut itself down.
+ WriteRegister(Register::RESYNC_REQUEST, 0);
ESP_ERROR_CHECK(i2s_channel_enable(i2s_handle_));
- WriteRegister(Register::POWER_MODE, 0);
+ i2s_active_ = true;
}
auto AudioDac::WriteData(const cpp::span<const std::byte>& data) -> void {
diff --git a/src/drivers/include/dac.hpp b/src/drivers/include/dac.hpp
index 4a1b2a5b..6849d92c 100644
--- a/src/drivers/include/dac.hpp
+++ b/src/drivers/include/dac.hpp
@@ -83,6 +83,7 @@ class AudioDac {
private:
GpioExpander* gpio_;
i2s_chan_handle_t i2s_handle_;
+ bool i2s_active_;
i2s_std_clk_config_t clock_config_;
i2s_std_slot_config_t slot_config_;
@@ -97,9 +98,13 @@ class AudioDac {
PAGE_SELECT = 0,
RESET = 1,
POWER_MODE = 2,
+ PLL_ENABLE = 4,
DE_EMPHASIS = 7,
+ PLL_CLOCK_SOURCE = 13,
DAC_CLOCK_SOURCE = 14,
+ RESYNC_REQUEST = 19,
CLOCK_ERRORS = 37,
+ INTERPOLATION = 34,
I2S_FORMAT = 40,
DIGITAL_VOLUME_L = 61,
DIGITAL_VOLUME_R = 62,
diff --git a/src/main/main.cpp b/src/main/main.cpp
index 98a571b6..77a1e14b 100644
--- a/src/main/main.cpp
+++ b/src/main/main.cpp
@@ -41,7 +41,7 @@
static const char* TAG = "MAIN";
void IRAM_ATTR tick_hook(void) {
- lv_tick_inc(1);
+ // lv_tick_inc(1);
}
static const size_t kLvglStackSize = 8 * 1024;
diff --git a/src/memory/include/himem.hpp b/src/memory/include/himem.hpp
index f71e912f..517ebfdf 100644
--- a/src/memory/include/himem.hpp
+++ b/src/memory/include/himem.hpp
@@ -57,21 +57,26 @@ class MappableRegion {
}
auto Get() -> cpp::span<std::byte> {
- if (bytes_ != nullptr) {
+ if (bytes_ == nullptr) {
return {};
}
return {bytes_, size};
}
auto Map(const HimemAlloc<size>& alloc) -> cpp::span<std::byte> {
- if (bytes_ != nullptr) {
- ESP_ERROR_CHECK(esp_himem_unmap(range_handle, bytes_, size));
- }
+ assert(bytes_ == nullptr);
ESP_ERROR_CHECK(esp_himem_map(alloc.handle, range_handle, 0, 0, size, 0,
reinterpret_cast<void**>(&bytes_)));
return Get();
}
+ auto Unmap() -> void {
+ if (bytes_ != nullptr) {
+ ESP_ERROR_CHECK(esp_himem_unmap(range_handle, bytes_, size));
+ bytes_ = nullptr;
+ }
+ }
+
// Not copyable or movable.
MappableRegion(const MappableRegion&) = delete;
MappableRegion& operator=(const MappableRegion&) = delete;