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
idf_component_register(
SRCS "audio_task.cpp" "fatfs_audio_input.cpp" "i2s_audio_output.cpp"
"track_queue.cpp" "audio_fsm.cpp" "sink_mixer.cpp" "resample.cpp"
SRCS "audio_decoder.cpp" "fatfs_audio_input.cpp" "i2s_audio_output.cpp"
"track_queue.cpp" "audio_fsm.cpp" "audio_converter.cpp" "resample.cpp"
"fatfs_source.cpp" "bt_audio_output.cpp"
INCLUDE_DIRS "include"
REQUIRES "codecs" "drivers" "cbor" "result" "tasks" "span" "memory" "tinyfsm"

@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "sink_mixer.hpp"
#include "audio_converter.hpp"
#include <algorithm>
#include <cmath>
@ -28,7 +28,7 @@ static constexpr std::size_t kSampleBufferLength = 240 * 2;
namespace audio {
SinkMixer::SinkMixer()
SampleConverter::SampleConverter()
: commands_(xQueueCreate(1, sizeof(Args))),
resampler_(nullptr),
source_(xStreamBufferCreateWithCaps(kSourceBufferLength,
@ -49,21 +49,21 @@ SinkMixer::SinkMixer()
tasks::StartPersistent<tasks::Type::kMixer>([&]() { Main(); });
}
SinkMixer::~SinkMixer() {
SampleConverter::~SampleConverter() {
vQueueDelete(commands_);
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
// not impacting performance given that the output will change only very
// rarely (if ever).
sink_ = output;
}
auto SinkMixer::MixAndSend(cpp::span<sample::Sample> input,
const IAudioOutput::Format& format,
bool is_eos) -> void {
auto SampleConverter::ConvertSamples(cpp::span<sample::Sample> input,
const IAudioOutput::Format& format,
bool is_eos) -> void {
Args args{
.format = format,
.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 (;;) {
Args args;
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)
-> size_t {
auto SampleConverter::HandleSamples(cpp::span<sample::Sample> input,
bool is_eos) -> size_t {
if (source_format_ == target_format_) {
// The happiest possible case: the input format matches the output
// format already.

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

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

@ -18,19 +18,21 @@
namespace audio {
/*
* Handles the final downmix + resample + quantisation stage of audio,
* generation sending the result directly to an IAudioOutput.
* Handle to a persistent task that converts samples between formats (sample
* 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:
SinkMixer();
~SinkMixer();
SampleConverter();
~SampleConverter();
auto SetOutput(std::shared_ptr<IAudioOutput>) -> void;
auto MixAndSend(cpp::span<sample::Sample>,
const IAudioOutput::Format& format,
bool is_eos) -> void;
auto ConvertSamples(cpp::span<sample::Sample>,
const IAudioOutput::Format& format,
bool is_eos) -> void;
private:
auto Main() -> void;

@ -9,15 +9,18 @@
#include <cstdint>
#include <memory>
#include "audio_converter.hpp"
#include "audio_sink.hpp"
#include "audio_source.hpp"
#include "codec.hpp"
#include "sink_mixer.hpp"
#include "track.hpp"
#include "types.hpp"
namespace audio {
/*
* Sample-based timer for the current elapsed playback time.
*/
class Timer {
public:
Timer(const codecs::ICodec::OutputFormat& format);
@ -32,25 +35,30 @@ class Timer {
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:
static auto Start(std::shared_ptr<IAudioSource> source,
std::shared_ptr<SinkMixer> mixer) -> AudioTask*;
std::shared_ptr<SampleConverter> converter) -> Decoder*;
auto Main() -> void;
AudioTask(const AudioTask&) = delete;
AudioTask& operator=(const AudioTask&) = delete;
Decoder(const Decoder&) = delete;
Decoder& operator=(const Decoder&) = delete;
private:
AudioTask(std::shared_ptr<IAudioSource> source,
std::shared_ptr<SinkMixer> mixer);
Decoder(std::shared_ptr<IAudioSource> source,
std::shared_ptr<SampleConverter> converter);
auto BeginDecoding(std::shared_ptr<codecs::IStream>) -> bool;
auto ContinueDecoding() -> bool;
std::shared_ptr<IAudioSource> source_;
std::shared_ptr<SinkMixer> mixer_;
std::shared_ptr<SampleConverter> converter_;
std::shared_ptr<codecs::IStream> stream_;
std::unique_ptr<codecs::ICodec> codec_;

@ -13,8 +13,8 @@
#include "audio_sink.hpp"
#include "tinyfsm.hpp"
#include "audio_decoder.hpp"
#include "audio_events.hpp"
#include "audio_task.hpp"
#include "bt_audio_output.hpp"
#include "database.hpp"
#include "display.hpp"
@ -68,9 +68,9 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
static std::shared_ptr<drivers::I2SDac> sDac;
static std::weak_ptr<database::Database> sDatabase;
static std::unique_ptr<AudioTask> sTask;
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 TrackQueue* sTrackQueue;

Loading…
Cancel
Save