diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-08-10 15:33:00 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-08-10 15:33:00 +1000 |
| commit | d8fc77101dcf80a3643a00b3446dca1e390ce997 (patch) | |
| tree | 9e03881f3857c7b4c6a0b6e3a062947daecc69d1 /src/audio/include | |
| parent | 67caeb6e3cda44205ba8fe783274b20dc7ea216e (diff) | |
| download | tangara-fw-d8fc77101dcf80a3643a00b3446dca1e390ce997.tar.gz | |
Give codecs complete control of their input files
Diffstat (limited to 'src/audio/include')
| -rw-r--r-- | src/audio/include/audio_fsm.hpp | 1 | ||||
| -rw-r--r-- | src/audio/include/audio_sink.hpp | 13 | ||||
| -rw-r--r-- | src/audio/include/audio_source.hpp | 24 | ||||
| -rw-r--r-- | src/audio/include/audio_task.hpp | 27 | ||||
| -rw-r--r-- | src/audio/include/fatfs_audio_input.hpp | 79 | ||||
| -rw-r--r-- | src/audio/include/i2s_audio_output.hpp | 6 | ||||
| -rw-r--r-- | src/audio/include/sink_mixer.hpp | 38 |
7 files changed, 58 insertions, 130 deletions
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 <stdint.h> +#include <cstdint> #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<void(Flags, InputStream&)>, TickType_t) - -> void = 0; + virtual auto HasNewStream() -> bool = 0; + virtual auto NextStream() -> std::shared_ptr<codecs::IStream> = 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<const std::byte>) -> bool; - - auto ConfigureSink(const StreamInfo::Pcm&, const Duration&) -> bool; - auto SendToSink(InputStream&) -> void; + auto BeginDecoding(std::shared_ptr<codecs::IStream>) -> bool; + auto ContinueDecoding() -> bool; IAudioSource* source_; IAudioSink* sink_; + + std::shared_ptr<codecs::IStream> stream_; std::unique_ptr<codecs::ICodec> codec_; std::unique_ptr<SinkMixer> mixer_; std::unique_ptr<Timer> timer_; - bool has_begun_decoding_; - std::optional<StreamInfo::Format> current_input_format_; - std::optional<StreamInfo::Pcm> current_output_format_; - std::optional<StreamInfo::Pcm> current_sink_format_; + std::optional<codecs::ICodec::OutputFormat> current_format_; + std::optional<IAudioSink::Format> current_sink_format_; - std::unique_ptr<RawStream> codec_buffer_; + cpp::span<sample::Sample> 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 <memory> #include <string> +#include "codec.hpp" #include "ff.h" #include "audio_source.hpp" @@ -24,54 +25,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<FIL>) -> 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<bool> has_data_; - std::unique_ptr<FIL> file_; - std::unique_ptr<FIL> next_file_; -}; - -/* * Audio source that fetches data from a FatFs (or exfat i guess) filesystem. * * All public methods are safe to call from any task. @@ -89,43 +42,27 @@ class FatfsAudioInput : public IAudioSource { auto SetPath(const std::string&) -> void; auto SetPath() -> void; - auto Read(std::function<void(Flags, InputStream&)>, TickType_t) - -> void override; + auto HasNewStream() -> bool override; + auto NextStream() -> std::shared_ptr<codecs::IStream> 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<codecs::StreamType>; - auto IsCurrentFormatMp3() -> bool; std::shared_ptr<database::ITagParser> 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<FileStreamer> streamer_; - - std::unique_ptr<RawStream> input_buffer_; + std::mutex new_stream_mutex_; + std::shared_ptr<codecs::IStream> 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<database::FutureFetcher<std::optional<std::string>>> 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<drivers::I2SDac> dac_; - std::optional<StreamInfo::Pcm> current_config_; + std::optional<Format> 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<sample::Sample>, + 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<sample::Sample>, bool) -> size_t; - auto Resample(InputStream&, OutputStream&) -> bool; auto ApplyDither(cpp::span<sample::Sample> samples, uint_fast8_t bits) -> void; - auto Downscale(cpp::span<sample::Sample>, cpp::span<int16_t>) -> 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> resampler_; - std::unique_ptr<RawStream> input_stream_; - std::unique_ptr<RawStream> resampled_stream_; - - StreamInfo::Pcm target_format_; StreamBufferHandle_t source_; - StreamBufferHandle_t sink_; + cpp::span<sample::Sample> input_buffer_; + cpp::span<std::byte> input_buffer_as_bytes_; + + cpp::span<sample::Sample> resampled_buffer_; + + IAudioSink* sink_; + IAudioSink::Format source_format_; + IAudioSink::Format target_format_; + size_t leftover_bytes_; + size_t leftover_offset_; }; } // namespace audio |
