Rename the main audio tasks to be more sensible
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user