From d8fc77101dcf80a3643a00b3446dca1e390ce997 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 10 Aug 2023 15:33:00 +1000 Subject: Give codecs complete control of their input files --- src/audio/include/audio_fsm.hpp | 1 + src/audio/include/audio_sink.hpp | 13 +++++- src/audio/include/audio_source.hpp | 24 +++------- src/audio/include/audio_task.hpp | 27 +++++------ src/audio/include/fatfs_audio_input.hpp | 79 ++++----------------------------- src/audio/include/i2s_audio_output.hpp | 6 +-- src/audio/include/sink_mixer.hpp | 38 ++++++++-------- 7 files changed, 58 insertions(+), 130 deletions(-) (limited to 'src/audio/include') diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp index cc3dae0e..d10f31e1 100644 --- a/src/audio/include/audio_fsm.hpp +++ b/src/audio/include/audio_fsm.hpp @@ -95,6 +95,7 @@ class Playback : public AudioState { void entry() override; void exit() override; + void react(const PlayFile&) override; void react(const QueueUpdate&) override; void react(const PlaybackUpdate&) override; diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp index 28acdc31..2fb4bf63 100644 --- a/src/audio/include/audio_sink.hpp +++ b/src/audio/include/audio_sink.hpp @@ -7,6 +7,7 @@ #pragma once #include +#include #include "audio_element.hpp" #include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" @@ -37,8 +38,16 @@ class IAudioSink { virtual auto AdjustVolumeUp() -> bool = 0; virtual auto AdjustVolumeDown() -> bool = 0; - virtual auto PrepareFormat(const StreamInfo::Pcm&) -> StreamInfo::Pcm = 0; - virtual auto Configure(const StreamInfo::Pcm& format) -> void = 0; + struct Format { + uint32_t sample_rate; + uint_fast8_t num_channels; + uint_fast8_t bits_per_sample; + + bool operator==(const Format&) const = default; + }; + + virtual auto PrepareFormat(const Format&) -> Format = 0; + virtual auto Configure(const Format& format) -> void = 0; auto stream() -> StreamBufferHandle_t { return stream_; } }; diff --git a/src/audio/include/audio_source.hpp b/src/audio/include/audio_source.hpp index 055a92cd..6c54a882 100644 --- a/src/audio/include/audio_source.hpp +++ b/src/audio/include/audio_source.hpp @@ -15,7 +15,10 @@ #include "freertos/portmacro.h" #include "freertos/semphr.h" +#include "codec.hpp" #include "stream_info.hpp" +#include "track.hpp" +#include "types.hpp" namespace audio { @@ -23,25 +26,8 @@ class IAudioSource { public: virtual ~IAudioSource() {} - class Flags { - public: - Flags(bool is_start, bool is_end) { - flags_[0] = is_start; - flags_[1] = is_end; - } - - auto is_start() -> bool { return flags_[0]; } - auto is_end() -> bool { return flags_[1]; } - - private: - std::bitset<2> flags_; - }; - - /* - * Synchronously fetches data from this source. - */ - virtual auto Read(std::function, TickType_t) - -> void = 0; + virtual auto HasNewStream() -> bool = 0; + virtual auto NextStream() -> std::shared_ptr = 0; }; } // namespace audio diff --git a/src/audio/include/audio_task.hpp b/src/audio/include/audio_task.hpp index b27aa039..48f5502c 100644 --- a/src/audio/include/audio_task.hpp +++ b/src/audio/include/audio_task.hpp @@ -16,6 +16,8 @@ #include "pipeline.hpp" #include "sink_mixer.hpp" #include "stream_info.hpp" +#include "track.hpp" +#include "types.hpp" namespace audio { @@ -52,32 +54,27 @@ class AudioTask { auto Main() -> void; + AudioTask(const AudioTask&) = delete; + AudioTask& operator=(const AudioTask&) = delete; + private: AudioTask(IAudioSource* source, IAudioSink* sink); - auto HandleNewStream(const InputStream&) -> bool; - - auto BeginDecoding(InputStream&) -> bool; - auto ContinueDecoding(InputStream&) -> bool; - auto FinishDecoding(InputStream&) -> void; - - auto ForwardPcmStream(StreamInfo::Pcm&, cpp::span) -> bool; - - auto ConfigureSink(const StreamInfo::Pcm&, const Duration&) -> bool; - auto SendToSink(InputStream&) -> void; + auto BeginDecoding(std::shared_ptr) -> bool; + auto ContinueDecoding() -> bool; IAudioSource* source_; IAudioSink* sink_; + + std::shared_ptr stream_; std::unique_ptr codec_; std::unique_ptr mixer_; std::unique_ptr timer_; - bool has_begun_decoding_; - std::optional current_input_format_; - std::optional current_output_format_; - std::optional current_sink_format_; + std::optional current_format_; + std::optional current_sink_format_; - std::unique_ptr codec_buffer_; + cpp::span codec_buffer_; }; } // namespace audio diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp index e13e49e2..df40696a 100644 --- a/src/audio/include/fatfs_audio_input.hpp +++ b/src/audio/include/fatfs_audio_input.hpp @@ -12,6 +12,7 @@ #include #include +#include "codec.hpp" #include "ff.h" #include "audio_source.hpp" @@ -23,54 +24,6 @@ namespace audio { -/* - * Handles coordination with a persistent background task to asynchronously - * read files from disk into a StreamBuffer. - */ -class FileStreamer { - public: - FileStreamer(StreamBufferHandle_t dest, SemaphoreHandle_t first_read); - ~FileStreamer(); - - /* - * Continues reading data into the destination buffer until the destination - * is full. - */ - auto Fetch() -> void; - - /* Returns true if the streamer has run out of data from the current file. */ - auto HasFinished() -> bool; - - /* - * Clears any remaining buffered data, and begins reading again from the - * given file. This function respects any seeking/reading that has already - * been done on the new source file. - */ - auto Restart(std::unique_ptr) -> void; - - FileStreamer(const FileStreamer&) = delete; - FileStreamer& operator=(const FileStreamer&) = delete; - - private: - // Note: private methods here should only be called from the streamer's task. - - auto Main() -> void; - auto CloseFile() -> void; - - enum Command { - kRestart, - kRefillBuffer, - kQuit, - }; - QueueHandle_t control_; - StreamBufferHandle_t destination_; - SemaphoreHandle_t data_was_read_; - - std::atomic has_data_; - std::unique_ptr file_; - std::unique_ptr next_file_; -}; - /* * Audio source that fetches data from a FatFs (or exfat i guess) filesystem. * @@ -89,43 +42,27 @@ class FatfsAudioInput : public IAudioSource { auto SetPath(const std::string&) -> void; auto SetPath() -> void; - auto Read(std::function, TickType_t) - -> void override; + auto HasNewStream() -> bool override; + auto NextStream() -> std::shared_ptr override; FatfsAudioInput(const FatfsAudioInput&) = delete; FatfsAudioInput& operator=(const FatfsAudioInput&) = delete; private: - // Note: private methods assume that the appropriate locks have already been - // acquired. - - auto OpenFile(const std::string& path) -> void; - auto CloseCurrentFile() -> void; - auto HasDataRemaining() -> bool; + auto OpenFile(const std::string& path) -> bool; - auto ContainerToStreamType(database::Encoding) + auto ContainerToStreamType(database::Container) -> std::optional; - auto IsCurrentFormatMp3() -> bool; std::shared_ptr tag_parser_; - // Semaphore used to block when this source is out of data. This should be - // acquired before attempting to read data, and returned after each incomplete - // read. - SemaphoreHandle_t has_data_; - - StreamBufferHandle_t streamer_buffer_; - std::unique_ptr streamer_; - - std::unique_ptr input_buffer_; + std::mutex new_stream_mutex_; + std::shared_ptr new_stream_; - // Mutex guarding the current file/stream associated with this source. Must be - // held during readings, and before altering the current file. - std::mutex source_mutex_; + SemaphoreHandle_t has_new_stream_; std::unique_ptr>> pending_path_; - bool is_first_read_; }; } // namespace audio diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp index e0f791c5..717e6519 100644 --- a/src/audio/include/i2s_audio_output.hpp +++ b/src/audio/include/i2s_audio_output.hpp @@ -35,8 +35,8 @@ class I2SAudioOutput : public IAudioSink { auto AdjustVolumeUp() -> bool override; auto AdjustVolumeDown() -> bool override; - auto PrepareFormat(const StreamInfo::Pcm&) -> StreamInfo::Pcm override; - auto Configure(const StreamInfo::Pcm& format) -> void override; + auto PrepareFormat(const Format&) -> Format override; + auto Configure(const Format& format) -> void override; I2SAudioOutput(const I2SAudioOutput&) = delete; I2SAudioOutput& operator=(const I2SAudioOutput&) = delete; @@ -45,7 +45,7 @@ class I2SAudioOutput : public IAudioSink { drivers::IGpios* expander_; std::shared_ptr dac_; - std::optional current_config_; + std::optional current_config_; int_fast8_t left_difference_; uint16_t current_volume_; uint16_t max_volume_; diff --git a/src/audio/include/sink_mixer.hpp b/src/audio/include/sink_mixer.hpp index d1e9aa8a..b4d25781 100644 --- a/src/audio/include/sink_mixer.hpp +++ b/src/audio/include/sink_mixer.hpp @@ -28,44 +28,42 @@ namespace audio { */ class SinkMixer { public: - SinkMixer(StreamBufferHandle_t dest); + SinkMixer(IAudioSink* sink); ~SinkMixer(); - auto MixAndSend(InputStream&, const StreamInfo::Pcm&) -> std::size_t; + auto MixAndSend(cpp::span, + const IAudioSink::Format& format, + bool is_eos) -> void; private: auto Main() -> void; auto SetTargetFormat(const StreamInfo::Pcm& format) -> void; - auto HandleBytes() -> void; + auto HandleSamples(cpp::span, bool) -> size_t; - auto Resample(InputStream&, OutputStream&) -> bool; auto ApplyDither(cpp::span samples, uint_fast8_t bits) -> void; - auto Downscale(cpp::span, cpp::span) -> void; - - enum class Command { - kReadBytes, - kSetSourceFormat, - kSetTargetFormat, - }; struct Args { - Command cmd; - StreamInfo::Pcm format; + IAudioSink::Format format; + size_t samples_available; + bool is_end_of_stream; }; - QueueHandle_t commands_; - SemaphoreHandle_t is_idle_; std::unique_ptr resampler_; - std::unique_ptr input_stream_; - std::unique_ptr resampled_stream_; - - StreamInfo::Pcm target_format_; StreamBufferHandle_t source_; - StreamBufferHandle_t sink_; + cpp::span input_buffer_; + cpp::span input_buffer_as_bytes_; + + cpp::span resampled_buffer_; + + IAudioSink* sink_; + IAudioSink::Format source_format_; + IAudioSink::Format target_format_; + size_t leftover_bytes_; + size_t leftover_offset_; }; } // namespace audio -- cgit v1.2.3