Rename the main audio tasks to be more sensible

custom
jacqueline 2 years ago
parent c635d5011c
commit f3c5eec025
  1. 4
      src/audio/CMakeLists.txt
  2. 20
      src/audio/audio_converter.cpp
  3. 31
      src/audio/audio_decoder.cpp
  4. 15
      src/audio/audio_fsm.cpp
  5. 18
      src/audio/include/audio_converter.hpp
  6. 24
      src/audio/include/audio_decoder.hpp
  7. 6
      src/audio/include/audio_fsm.hpp

@ -3,8 +3,8 @@
# SPDX-License-Identifier: GPL-3.0-only # SPDX-License-Identifier: GPL-3.0-only
idf_component_register( idf_component_register(
SRCS "audio_task.cpp" "fatfs_audio_input.cpp" "i2s_audio_output.cpp" SRCS "audio_decoder.cpp" "fatfs_audio_input.cpp" "i2s_audio_output.cpp"
"track_queue.cpp" "audio_fsm.cpp" "sink_mixer.cpp" "resample.cpp" "track_queue.cpp" "audio_fsm.cpp" "audio_converter.cpp" "resample.cpp"
"fatfs_source.cpp" "bt_audio_output.cpp" "fatfs_source.cpp" "bt_audio_output.cpp"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
REQUIRES "codecs" "drivers" "cbor" "result" "tasks" "span" "memory" "tinyfsm" REQUIRES "codecs" "drivers" "cbor" "result" "tasks" "span" "memory" "tinyfsm"

@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only * SPDX-License-Identifier: GPL-3.0-only
*/ */
#include "sink_mixer.hpp" #include "audio_converter.hpp"
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@ -28,7 +28,7 @@ static constexpr std::size_t kSampleBufferLength = 240 * 2;
namespace audio { namespace audio {
SinkMixer::SinkMixer() SampleConverter::SampleConverter()
: commands_(xQueueCreate(1, sizeof(Args))), : commands_(xQueueCreate(1, sizeof(Args))),
resampler_(nullptr), resampler_(nullptr),
source_(xStreamBufferCreateWithCaps(kSourceBufferLength, source_(xStreamBufferCreateWithCaps(kSourceBufferLength,
@ -49,21 +49,21 @@ SinkMixer::SinkMixer()
tasks::StartPersistent<tasks::Type::kMixer>([&]() { Main(); }); tasks::StartPersistent<tasks::Type::kMixer>([&]() { Main(); });
} }
SinkMixer::~SinkMixer() { SampleConverter::~SampleConverter() {
vQueueDelete(commands_); vQueueDelete(commands_);
vStreamBufferDelete(source_); vStreamBufferDelete(source_);
} }
auto SinkMixer::SetOutput(std::shared_ptr<IAudioOutput> output) -> void { auto SampleConverter::SetOutput(std::shared_ptr<IAudioOutput> output) -> void {
// FIXME: We should add synchronisation here, but we should be careful about // FIXME: We should add synchronisation here, but we should be careful about
// not impacting performance given that the output will change only very // not impacting performance given that the output will change only very
// rarely (if ever). // rarely (if ever).
sink_ = output; sink_ = output;
} }
auto SinkMixer::MixAndSend(cpp::span<sample::Sample> input, auto SampleConverter::ConvertSamples(cpp::span<sample::Sample> input,
const IAudioOutput::Format& format, const IAudioOutput::Format& format,
bool is_eos) -> void { bool is_eos) -> void {
Args args{ Args args{
.format = format, .format = format,
.samples_available = input.size(), .samples_available = input.size(),
@ -81,7 +81,7 @@ auto SinkMixer::MixAndSend(cpp::span<sample::Sample> input,
} }
} }
auto SinkMixer::Main() -> void { auto SampleConverter::Main() -> void {
for (;;) { for (;;) {
Args args; Args args;
while (!xQueueReceive(commands_, &args, portMAX_DELAY)) { while (!xQueueReceive(commands_, &args, portMAX_DELAY)) {
@ -149,8 +149,8 @@ auto SinkMixer::Main() -> void {
} }
} }
auto SinkMixer::HandleSamples(cpp::span<sample::Sample> input, bool is_eos) auto SampleConverter::HandleSamples(cpp::span<sample::Sample> input,
-> size_t { bool is_eos) -> size_t {
if (source_format_ == target_format_) { if (source_format_ == target_format_) {
// The happiest possible case: the input format matches the output // The happiest possible case: the input format matches the output
// format already. // format already.

@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only * SPDX-License-Identifier: GPL-3.0-only
*/ */
#include "audio_task.hpp" #include "audio_decoder.hpp"
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
@ -28,6 +28,7 @@
#include "freertos/ringbuf.h" #include "freertos/ringbuf.h"
#include "span.hpp" #include "span.hpp"
#include "audio_converter.hpp"
#include "audio_events.hpp" #include "audio_events.hpp"
#include "audio_fsm.hpp" #include "audio_fsm.hpp"
#include "audio_sink.hpp" #include "audio_sink.hpp"
@ -36,7 +37,6 @@
#include "event_queue.hpp" #include "event_queue.hpp"
#include "fatfs_audio_input.hpp" #include "fatfs_audio_input.hpp"
#include "sample.hpp" #include "sample.hpp"
#include "sink_mixer.hpp"
#include "tasks.hpp" #include "tasks.hpp"
#include "track.hpp" #include "track.hpp"
#include "types.hpp" #include "types.hpp"
@ -76,23 +76,27 @@ auto Timer::AddSamples(std::size_t samples) -> void {
} }
} }
auto AudioTask::Start(std::shared_ptr<IAudioSource> source, auto Decoder::Start(std::shared_ptr<IAudioSource> source,
std::shared_ptr<SinkMixer> sink) -> AudioTask* { std::shared_ptr<SampleConverter> sink) -> Decoder* {
AudioTask* task = new AudioTask(source, sink); Decoder* task = new Decoder(source, sink);
tasks::StartPersistent<tasks::Type::kAudio>([=]() { task->Main(); }); tasks::StartPersistent<tasks::Type::kAudio>([=]() { task->Main(); });
return task; return task;
} }
AudioTask::AudioTask(std::shared_ptr<IAudioSource> source, Decoder::Decoder(std::shared_ptr<IAudioSource> source,
std::shared_ptr<SinkMixer> mixer) std::shared_ptr<SampleConverter> mixer)
: source_(source), mixer_(mixer), codec_(), timer_(), current_format_() { : source_(source),
converter_(mixer),
codec_(),
timer_(),
current_format_() {
codec_buffer_ = { codec_buffer_ = {
reinterpret_cast<sample::Sample*>(heap_caps_calloc( reinterpret_cast<sample::Sample*>(heap_caps_calloc(
kCodecBufferLength, sizeof(sample::Sample), MALLOC_CAP_SPIRAM)), kCodecBufferLength, sizeof(sample::Sample), MALLOC_CAP_SPIRAM)),
kCodecBufferLength}; kCodecBufferLength};
} }
void AudioTask::Main() { void Decoder::Main() {
for (;;) { for (;;) {
if (source_->HasNewStream() || !stream_) { if (source_->HasNewStream() || !stream_) {
std::shared_ptr<codecs::IStream> new_stream = source_->NextStream(); std::shared_ptr<codecs::IStream> new_stream = source_->NextStream();
@ -110,7 +114,7 @@ void AudioTask::Main() {
} }
} }
auto AudioTask::BeginDecoding(std::shared_ptr<codecs::IStream> stream) -> bool { auto Decoder::BeginDecoding(std::shared_ptr<codecs::IStream> stream) -> bool {
codec_.reset(codecs::CreateCodecForType(stream->type()).value_or(nullptr)); codec_.reset(codecs::CreateCodecForType(stream->type()).value_or(nullptr));
if (!codec_) { if (!codec_) {
ESP_LOGE(kTag, "no codec found"); ESP_LOGE(kTag, "no codec found");
@ -140,15 +144,16 @@ auto AudioTask::BeginDecoding(std::shared_ptr<codecs::IStream> stream) -> bool {
return true; return true;
} }
auto AudioTask::ContinueDecoding() -> bool { auto Decoder::ContinueDecoding() -> bool {
auto res = codec_->DecodeTo(codec_buffer_); auto res = codec_->DecodeTo(codec_buffer_);
if (res.has_error()) { if (res.has_error()) {
return true; return true;
} }
if (res->samples_written > 0) { if (res->samples_written > 0) {
mixer_->MixAndSend(codec_buffer_.first(res->samples_written), converter_->ConvertSamples(codec_buffer_.first(res->samples_written),
current_sink_format_.value(), res->is_stream_finished); current_sink_format_.value(),
res->is_stream_finished);
} }
if (timer_) { if (timer_) {

@ -15,8 +15,9 @@
#include "freertos/portmacro.h" #include "freertos/portmacro.h"
#include "freertos/projdefs.h" #include "freertos/projdefs.h"
#include "audio_converter.hpp"
#include "audio_decoder.hpp"
#include "audio_events.hpp" #include "audio_events.hpp"
#include "audio_task.hpp"
#include "bluetooth.hpp" #include "bluetooth.hpp"
#include "bt_audio_output.hpp" #include "bt_audio_output.hpp"
#include "event_queue.hpp" #include "event_queue.hpp"
@ -24,7 +25,6 @@
#include "future_fetcher.hpp" #include "future_fetcher.hpp"
#include "i2s_audio_output.hpp" #include "i2s_audio_output.hpp"
#include "i2s_dac.hpp" #include "i2s_dac.hpp"
#include "sink_mixer.hpp"
#include "system_events.hpp" #include "system_events.hpp"
#include "track.hpp" #include "track.hpp"
#include "track_queue.hpp" #include "track_queue.hpp"
@ -37,10 +37,9 @@ drivers::IGpios* AudioState::sIGpios;
std::shared_ptr<drivers::I2SDac> AudioState::sDac; std::shared_ptr<drivers::I2SDac> AudioState::sDac;
std::weak_ptr<database::Database> AudioState::sDatabase; std::weak_ptr<database::Database> AudioState::sDatabase;
std::unique_ptr<AudioTask> AudioState::sTask;
std::shared_ptr<FatfsAudioInput> AudioState::sFileSource; std::shared_ptr<FatfsAudioInput> AudioState::sFileSource;
std::shared_ptr<SinkMixer> AudioState::sMixer; std::unique_ptr<Decoder> AudioState::sDecoder;
std::shared_ptr<SampleConverter> AudioState::sSampleConverter;
std::shared_ptr<IAudioOutput> AudioState::sOutput; std::shared_ptr<IAudioOutput> AudioState::sOutput;
TrackQueue* AudioState::sTrackQueue; TrackQueue* AudioState::sTrackQueue;
@ -65,10 +64,10 @@ auto AudioState::Init(drivers::IGpios* gpio_expander,
sOutput.reset(new I2SAudioOutput(sIGpios, sDac)); sOutput.reset(new I2SAudioOutput(sIGpios, sDac));
// sOutput.reset(new BluetoothAudioOutput(bluetooth)); // sOutput.reset(new BluetoothAudioOutput(bluetooth));
sMixer.reset(new SinkMixer()); sSampleConverter.reset(new SampleConverter());
sMixer->SetOutput(sOutput); sSampleConverter->SetOutput(sOutput);
AudioTask::Start(sFileSource, sMixer); Decoder::Start(sFileSource, sSampleConverter);
return true; return true;
} }

@ -18,19 +18,21 @@
namespace audio { namespace audio {
/* /*
* Handles the final downmix + resample + quantisation stage of audio, * Handle to a persistent task that converts samples between formats (sample
* generation sending the result directly to an IAudioOutput. * rate, channels, bits per sample), in order to put samples in the preferred
* format of the current output device. The resulting samples are forwarded
* to the output device's sink stream.
*/ */
class SinkMixer { class SampleConverter {
public: public:
SinkMixer(); SampleConverter();
~SinkMixer(); ~SampleConverter();
auto SetOutput(std::shared_ptr<IAudioOutput>) -> void; auto SetOutput(std::shared_ptr<IAudioOutput>) -> void;
auto MixAndSend(cpp::span<sample::Sample>, auto ConvertSamples(cpp::span<sample::Sample>,
const IAudioOutput::Format& format, const IAudioOutput::Format& format,
bool is_eos) -> void; bool is_eos) -> void;
private: private:
auto Main() -> void; auto Main() -> void;

@ -9,15 +9,18 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include "audio_converter.hpp"
#include "audio_sink.hpp" #include "audio_sink.hpp"
#include "audio_source.hpp" #include "audio_source.hpp"
#include "codec.hpp" #include "codec.hpp"
#include "sink_mixer.hpp"
#include "track.hpp" #include "track.hpp"
#include "types.hpp" #include "types.hpp"
namespace audio { namespace audio {
/*
* Sample-based timer for the current elapsed playback time.
*/
class Timer { class Timer {
public: public:
Timer(const codecs::ICodec::OutputFormat& format); Timer(const codecs::ICodec::OutputFormat& format);
@ -32,25 +35,30 @@ class Timer {
uint32_t total_duration_seconds_; uint32_t total_duration_seconds_;
}; };
class AudioTask { /*
* Handle to a persistent task that takes bytes from the given source, decodes
* them into sample::Sample (normalised to 16 bit signed PCM), and then
* forwards the resulting stream to the given converter.
*/
class Decoder {
public: public:
static auto Start(std::shared_ptr<IAudioSource> source, static auto Start(std::shared_ptr<IAudioSource> source,
std::shared_ptr<SinkMixer> mixer) -> AudioTask*; std::shared_ptr<SampleConverter> converter) -> Decoder*;
auto Main() -> void; auto Main() -> void;
AudioTask(const AudioTask&) = delete; Decoder(const Decoder&) = delete;
AudioTask& operator=(const AudioTask&) = delete; Decoder& operator=(const Decoder&) = delete;
private: private:
AudioTask(std::shared_ptr<IAudioSource> source, Decoder(std::shared_ptr<IAudioSource> source,
std::shared_ptr<SinkMixer> mixer); std::shared_ptr<SampleConverter> converter);
auto BeginDecoding(std::shared_ptr<codecs::IStream>) -> bool; auto BeginDecoding(std::shared_ptr<codecs::IStream>) -> bool;
auto ContinueDecoding() -> bool; auto ContinueDecoding() -> bool;
std::shared_ptr<IAudioSource> source_; std::shared_ptr<IAudioSource> source_;
std::shared_ptr<SinkMixer> mixer_; std::shared_ptr<SampleConverter> converter_;
std::shared_ptr<codecs::IStream> stream_; std::shared_ptr<codecs::IStream> stream_;
std::unique_ptr<codecs::ICodec> codec_; std::unique_ptr<codecs::ICodec> codec_;

@ -13,8 +13,8 @@
#include "audio_sink.hpp" #include "audio_sink.hpp"
#include "tinyfsm.hpp" #include "tinyfsm.hpp"
#include "audio_decoder.hpp"
#include "audio_events.hpp" #include "audio_events.hpp"
#include "audio_task.hpp"
#include "bt_audio_output.hpp" #include "bt_audio_output.hpp"
#include "database.hpp" #include "database.hpp"
#include "display.hpp" #include "display.hpp"
@ -68,9 +68,9 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
static std::shared_ptr<drivers::I2SDac> sDac; static std::shared_ptr<drivers::I2SDac> sDac;
static std::weak_ptr<database::Database> sDatabase; static std::weak_ptr<database::Database> sDatabase;
static std::unique_ptr<AudioTask> sTask;
static std::shared_ptr<FatfsAudioInput> sFileSource; static std::shared_ptr<FatfsAudioInput> sFileSource;
static std::shared_ptr<SinkMixer> sMixer; static std::unique_ptr<Decoder> sDecoder;
static std::shared_ptr<SampleConverter> sSampleConverter;
static std::shared_ptr<IAudioOutput> sOutput; static std::shared_ptr<IAudioOutput> sOutput;
static TrackQueue* sTrackQueue; static TrackQueue* sTrackQueue;

Loading…
Cancel
Save