diff options
Diffstat (limited to 'src/audio/include')
| -rw-r--r-- | src/audio/include/audio_converter.hpp | 21 | ||||
| -rw-r--r-- | src/audio/include/audio_decoder.hpp | 20 | ||||
| -rw-r--r-- | src/audio/include/audio_events.hpp | 105 | ||||
| -rw-r--r-- | src/audio/include/audio_fsm.hpp | 54 | ||||
| -rw-r--r-- | src/audio/include/audio_sink.hpp | 1 |
5 files changed, 118 insertions, 83 deletions
diff --git a/src/audio/include/audio_converter.hpp b/src/audio/include/audio_converter.hpp index c2ebde60..232b5d8e 100644 --- a/src/audio/include/audio_converter.hpp +++ b/src/audio/include/audio_converter.hpp @@ -6,9 +6,11 @@ #pragma once +#include <stdint.h> #include <cstdint> #include <memory> +#include "audio_events.hpp" #include "audio_sink.hpp" #include "audio_source.hpp" #include "codec.hpp" @@ -30,18 +32,23 @@ class SampleConverter { auto SetOutput(std::shared_ptr<IAudioOutput>) -> void; - auto ConvertSamples(cpp::span<sample::Sample>, - const IAudioOutput::Format& format, - bool is_eos) -> void; + auto beginStream(std::shared_ptr<TrackInfo>) -> void; + auto continueStream(cpp::span<sample::Sample>) -> void; + auto endStream() -> void; private: auto Main() -> void; - auto SetTargetFormat(const IAudioOutput::Format& format) -> void; - auto HandleSamples(cpp::span<sample::Sample>, bool) -> size_t; + auto handleBeginStream(std::shared_ptr<TrackInfo>) -> void; + auto handleContinueStream(size_t samples_available) -> void; + auto handleEndStream() -> void; + + auto handleSamples(cpp::span<sample::Sample>) -> size_t; + + auto sendToSink(cpp::span<sample::Sample>) -> void; struct Args { - IAudioOutput::Format format; + std::shared_ptr<TrackInfo>* track; size_t samples_available; bool is_end_of_stream; }; @@ -59,6 +66,8 @@ class SampleConverter { IAudioOutput::Format source_format_; IAudioOutput::Format target_format_; size_t leftover_bytes_; + + uint32_t samples_sunk_; }; } // namespace audio diff --git a/src/audio/include/audio_decoder.hpp b/src/audio/include/audio_decoder.hpp index b8aac710..89f0f43c 100644 --- a/src/audio/include/audio_decoder.hpp +++ b/src/audio/include/audio_decoder.hpp @@ -20,25 +20,6 @@ namespace audio { /* - * Sample-based timer for the current elapsed playback time. - */ -class Timer { - public: - Timer(std::shared_ptr<Track>, const codecs::ICodec::OutputFormat& format, uint32_t current_seconds = 0); - - auto AddSamples(std::size_t) -> void; - - private: - std::shared_ptr<Track> track_; - - uint32_t current_seconds_; - uint32_t current_sample_in_second_; - uint32_t samples_per_second_; - - uint32_t total_duration_seconds_; -}; - -/* * 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. @@ -65,7 +46,6 @@ class Decoder { std::shared_ptr<codecs::IStream> stream_; std::unique_ptr<codecs::ICodec> codec_; - std::unique_ptr<Timer> timer_; std::optional<codecs::ICodec::OutputFormat> current_format_; std::optional<IAudioOutput::Format> current_sink_format_; diff --git a/src/audio/include/audio_events.hpp b/src/audio/include/audio_events.hpp index a8533646..b8a0dba6 100644 --- a/src/audio/include/audio_events.hpp +++ b/src/audio/include/audio_events.hpp @@ -9,8 +9,10 @@ #include <stdint.h> #include <cstdint> #include <memory> +#include <optional> #include <string> +#include "audio_sink.hpp" #include "tinyfsm.hpp" #include "track.hpp" @@ -18,24 +20,82 @@ namespace audio { -struct Track { +/* + * Struct encapsulating information about the decoder's current track. + */ +struct TrackInfo { + /* + * Audio tags extracted from the file. May be absent for files without any + * parseable tags. + */ std::shared_ptr<database::TrackTags> tags; - std::shared_ptr<database::TrackData> db_info; - uint32_t duration; - uint32_t bitrate_kbps; + /* + * URI that the current track was retrieved from. This is currently always a + * file path on the SD card. + */ + std::string uri; + + /* + * The length of this track in seconds. This is either retrieved from the + * track's tags, or sometimes computed. It may therefore sometimes be + * inaccurate or missing. + */ + std::optional<uint32_t> duration; + + /* The offset in seconds that this file's decoding started from. */ + std::optional<uint32_t> start_offset; + + /* The approximate bitrate of this track in its original encoded form. */ + std::optional<uint32_t> bitrate_kbps; + + /* The encoded format of the this track. */ codecs::StreamType encoding; - std::string filepath; -}; -struct PlaybackStarted : tinyfsm::Event {}; + IAudioOutput::Format format; +}; +/* + * Event emitted by the audio FSM when the state of the audio pipeline has + * changed. This is usually once per second while a track is playing, plus one + * event each when a track starts or finishes. + */ struct PlaybackUpdate : tinyfsm::Event { - uint32_t seconds_elapsed; - std::shared_ptr<Track> track; + /* + * The track that is currently being decoded by the audio pipeline. May be + * absent if there is no current track. + */ + std::shared_ptr<TrackInfo> current_track; + + /* + * How long the current track has been playing for, in seconds. Will always + * be present if current_track is present. + */ + std::optional<uint32_t> track_position; + + /* Whether or not the current track is currently being output to a sink. */ + bool paused; +}; + +/* + * Sets a new track to be decoded by the audio pipeline, replacing any + * currently playing track. + */ +struct SetTrack : tinyfsm::Event { + std::variant<std::string, database::TrackId, std::monostate> new_track; + std::optional<uint32_t> seek_to_second; + + enum Transition { + kHardCut, + kGapless, + // TODO: kCrossFade + }; + Transition transition; }; -struct PlaybackStopped : tinyfsm::Event {}; +struct TogglePlayPause : tinyfsm::Event { + std::optional<bool> set_to; +}; struct QueueUpdate : tinyfsm::Event { bool current_changed; @@ -49,15 +109,6 @@ struct QueueUpdate : tinyfsm::Event { Reason reason; }; -struct PlayFile : tinyfsm::Event { - std::string filename; -}; - -struct SeekFile : tinyfsm::Event { - uint32_t offset; - std::string filename; -}; - struct StepUpVolume : tinyfsm::Event {}; struct StepDownVolume : tinyfsm::Event {}; struct SetVolume : tinyfsm::Event { @@ -83,17 +134,21 @@ struct SetVolumeLimit : tinyfsm::Event { int limit_db; }; -struct TogglePlayPause : tinyfsm::Event {}; - struct OutputModeChanged : tinyfsm::Event {}; namespace internal { -struct InputFileOpened : tinyfsm::Event {}; -struct InputFileClosed : tinyfsm::Event {}; -struct InputFileFinished : tinyfsm::Event {}; +struct StreamStarted : tinyfsm::Event { + std::shared_ptr<TrackInfo> track; + IAudioOutput::Format src_format; + IAudioOutput::Format dst_format; +}; + +struct StreamUpdate : tinyfsm::Event { + uint32_t samples_sunk; +}; -struct AudioPipelineIdle : tinyfsm::Event {}; +struct StreamEnded : tinyfsm::Event {}; } // namespace internal diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp index 13e241be..60afb321 100644 --- a/src/audio/include/audio_fsm.hpp +++ b/src/audio/include/audio_fsm.hpp @@ -6,6 +6,7 @@ #pragma once +#include <stdint.h> #include <deque> #include <memory> #include <vector> @@ -41,6 +42,14 @@ class AudioState : public tinyfsm::Fsm<AudioState> { /* Fallback event handler. Does nothing. */ void react(const tinyfsm::Event& ev) {} + void react(const QueueUpdate&); + void react(const SetTrack&); + void react(const TogglePlayPause&); + + void react(const internal::StreamStarted&); + void react(const internal::StreamUpdate&); + void react(const internal::StreamEnded&); + void react(const StepUpVolume&); void react(const StepDownVolume&); virtual void react(const system_fsm::HasPhonesChanged&); @@ -56,19 +65,10 @@ class AudioState : public tinyfsm::Fsm<AudioState> { virtual void react(const system_fsm::StorageMounted&) {} virtual void react(const system_fsm::BluetoothEvent&); - virtual void react(const PlayFile&) {} - virtual void react(const SeekFile&) {} - virtual void react(const QueueUpdate&) {} - virtual void react(const PlaybackUpdate&) {} - void react(const TogglePlayPause&); - - virtual void react(const internal::InputFileOpened&) {} - virtual void react(const internal::InputFileClosed&) {} - virtual void react(const internal::InputFileFinished&) {} - virtual void react(const internal::AudioPipelineIdle&) {} - protected: auto clearDrainBuffer() -> void; + auto awaitEmptyDrainBuffer() -> void; + auto playTrack(database::TrackId id) -> void; auto commitVolume() -> void; @@ -83,10 +83,19 @@ class AudioState : public tinyfsm::Fsm<AudioState> { static StreamBufferHandle_t sDrainBuffer; - static std::optional<database::TrackId> sCurrentTrack; + static std::shared_ptr<TrackInfo> sCurrentTrack; + static uint64_t sCurrentSamples; + static std::optional<IAudioOutput::Format> sDrainFormat; + static bool sCurrentTrackIsFromQueue; - auto readyToPlay() -> bool; - static bool sIsPlaybackAllowed; + static std::shared_ptr<TrackInfo> sNextTrack; + static uint64_t sNextTrackCueSamples; + static bool sNextTrackIsFromQueue; + + static bool sIsResampling; + static bool sIsPaused; + + auto currentPositionSeconds() -> std::optional<uint32_t>; }; namespace states { @@ -94,7 +103,6 @@ namespace states { class Uninitialised : public AudioState { public: void react(const system_fsm::BootComplete&) override; - void react(const system_fsm::BluetoothEvent&) override{}; using AudioState::react; @@ -102,10 +110,6 @@ class Uninitialised : public AudioState { class Standby : public AudioState { public: - void react(const PlayFile&) override; - void react(const SeekFile&) override; - void react(const internal::InputFileOpened&) override; - void react(const QueueUpdate&) override; void react(const system_fsm::KeyLockChanged&) override; void react(const system_fsm::StorageMounted&) override; @@ -117,18 +121,6 @@ class Playback : public AudioState { void entry() override; void exit() override; - void react(const system_fsm::HasPhonesChanged&) override; - - void react(const PlayFile&) override; - void react(const SeekFile&) override; - void react(const QueueUpdate&) override; - void react(const PlaybackUpdate&) override; - - void react(const internal::InputFileOpened&) override; - void react(const internal::InputFileClosed&) override; - void react(const internal::InputFileFinished&) override; - void react(const internal::AudioPipelineIdle&) override; - using AudioState::react; }; diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp index 85c23f5c..e11f3ce0 100644 --- a/src/audio/include/audio_sink.hpp +++ b/src/audio/include/audio_sink.hpp @@ -11,7 +11,6 @@ #include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" -#include "idf_additions.h" namespace audio { |
