diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-07-07 15:29:47 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-07-07 15:29:47 +1000 |
| commit | 39f7545cd5ef7a30bbd482f3579df7744c6b688d (patch) | |
| tree | a760a50cc17365fbcd69eb89ca627ad7feb8c0b6 /src/audio/include | |
| parent | 2f16d230025c3173cfbecc58b38d6a52b6b0f5f2 (diff) | |
| download | tangara-fw-39f7545cd5ef7a30bbd482f3579df7744c6b688d.tar.gz | |
wire up the playing screen with some real data
Includes implementing song duration calculation for CBR MP3 files
Diffstat (limited to 'src/audio/include')
| -rw-r--r-- | src/audio/include/audio_decoder.hpp | 3 | ||||
| -rw-r--r-- | src/audio/include/audio_events.hpp | 21 | ||||
| -rw-r--r-- | src/audio/include/audio_fsm.hpp | 33 | ||||
| -rw-r--r-- | src/audio/include/fatfs_audio_input.hpp | 2 | ||||
| -rw-r--r-- | src/audio/include/stream_info.hpp | 9 | ||||
| -rw-r--r-- | src/audio/include/track_queue.hpp | 85 |
6 files changed, 125 insertions, 28 deletions
diff --git a/src/audio/include/audio_decoder.hpp b/src/audio/include/audio_decoder.hpp index aa051685..a6b4754a 100644 --- a/src/audio/include/audio_decoder.hpp +++ b/src/audio/include/audio_decoder.hpp @@ -40,8 +40,9 @@ class AudioDecoder : public IAudioElement { private: std::unique_ptr<codecs::ICodec> current_codec_; - std::optional<StreamInfo::Format> current_input_format_; + std::optional<StreamInfo::Encoded> current_input_format_; std::optional<StreamInfo::Format> current_output_format_; + std::optional<std::size_t> duration_seconds_from_decoder_; std::optional<std::size_t> seek_to_sample_; bool has_prepared_output_; bool has_samples_to_send_; diff --git a/src/audio/include/audio_events.hpp b/src/audio/include/audio_events.hpp index 019b65a2..8af3703a 100644 --- a/src/audio/include/audio_events.hpp +++ b/src/audio/include/audio_events.hpp @@ -13,26 +13,31 @@ #include "tinyfsm.hpp" #include "track.hpp" +#include "track_queue.hpp" namespace audio { -struct PlayFile : tinyfsm::Event { - std::string filename; -}; - -struct PlayTrack : tinyfsm::Event { - database::TrackId id; - std::optional<database::TrackData> data; +struct PlaybackStarted : tinyfsm::Event { + database::Track track; }; struct PlaybackUpdate : tinyfsm::Event { uint32_t seconds_elapsed; + uint32_t seconds_total; }; +struct QueueUpdate : tinyfsm::Event {}; + +struct VolumeChanged : tinyfsm::Event {}; + +namespace internal { + struct InputFileOpened : tinyfsm::Event {}; +struct InputFileClosed : tinyfsm::Event {}; struct InputFileFinished : tinyfsm::Event {}; + struct AudioPipelineIdle : tinyfsm::Event {}; -struct VolumeChanged : tinyfsm::Event {}; +} // namespace internal } // namespace audio diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp index dadbd072..7910f4e2 100644 --- a/src/audio/include/audio_fsm.hpp +++ b/src/audio/include/audio_fsm.hpp @@ -22,13 +22,15 @@ #include "track.hpp" #include "system_events.hpp" +#include "track_queue.hpp" namespace audio { class AudioState : public tinyfsm::Fsm<AudioState> { public: static auto Init(drivers::IGpios* gpio_expander, - std::weak_ptr<database::Database>) -> bool; + std::weak_ptr<database::Database>, + TrackQueue* queue) -> bool; virtual ~AudioState() {} @@ -45,14 +47,14 @@ class AudioState : public tinyfsm::Fsm<AudioState> { void react(const system_fsm::HasPhonesChanged&); virtual void react(const system_fsm::BootComplete&) {} - virtual void react(const PlayTrack&) {} - virtual void react(const PlayFile&) {} + virtual void react(const QueueUpdate&) {} virtual void react(const PlaybackUpdate&) {} - virtual void react(const InputFileOpened&) {} - virtual void react(const InputFileFinished&) {} - virtual void react(const AudioPipelineIdle&) {} + 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: static drivers::IGpios* sIGpios; @@ -63,8 +65,7 @@ class AudioState : public tinyfsm::Fsm<AudioState> { static std::unique_ptr<I2SAudioOutput> sI2SOutput; static std::vector<std::unique_ptr<IAudioElement>> sPipeline; - typedef std::variant<database::TrackId, std::string> EnqueuedItem; - static std::deque<EnqueuedItem> sTrackQueue; + static TrackQueue* sTrackQueue; }; namespace states { @@ -77,9 +78,8 @@ class Uninitialised : public AudioState { class Standby : public AudioState { public: - void react(const InputFileOpened&) override; - void react(const PlayTrack&) override; - void react(const PlayFile&) override; + void react(const internal::InputFileOpened&) override; + void react(const QueueUpdate&) override; using AudioState::react; }; @@ -89,14 +89,13 @@ class Playback : public AudioState { void entry() override; void exit() override; - void react(const PlayTrack&) override; - void react(const PlayFile&) override; - + void react(const QueueUpdate&) override; void react(const PlaybackUpdate&) override; - void react(const InputFileOpened&) override; - void react(const InputFileFinished&) override; - void react(const AudioPipelineIdle&) 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/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp index 77d3b96d..56f92fcf 100644 --- a/src/audio/include/fatfs_audio_input.hpp +++ b/src/audio/include/fatfs_audio_input.hpp @@ -34,6 +34,7 @@ class FatfsAudioInput : public IAudioElement { FatfsAudioInput(); ~FatfsAudioInput(); + auto CurrentFile() -> std::optional<std::string> { return current_path_; } auto OpenFile(std::future<std::optional<std::string>>&& path) -> void; auto OpenFile(const std::string& path) -> bool; @@ -50,6 +51,7 @@ class FatfsAudioInput : public IAudioElement { -> std::optional<codecs::StreamType>; std::optional<std::future<std::optional<std::string>>> pending_path_; + std::optional<std::string> current_path_; FIL current_file_; bool is_file_open_; bool has_prepared_output_; diff --git a/src/audio/include/stream_info.hpp b/src/audio/include/stream_info.hpp index 4db3e5fd..69bf3c4b 100644 --- a/src/audio/include/stream_info.hpp +++ b/src/audio/include/stream_info.hpp @@ -30,13 +30,16 @@ struct StreamInfo { bool is_consumer_finished = true; - // - std::optional<uint32_t> seek_to_seconds{}; + std::optional<std::uint32_t> duration_seconds; + + std::optional<std::uint32_t> seek_to_seconds{}; struct Encoded { // The codec that this stream is associated with. codecs::StreamType type; + std::optional<std::size_t> duration_bytes; + bool operator==(const Encoded&) const = default; }; @@ -95,6 +98,8 @@ class OutputStream { bool prepare(const StreamInfo::Format& new_format); + void set_duration(std::size_t); + const StreamInfo& info() const; cpp::span<std::byte> data() const; diff --git a/src/audio/include/track_queue.hpp b/src/audio/include/track_queue.hpp new file mode 100644 index 00000000..840d71ee --- /dev/null +++ b/src/audio/include/track_queue.hpp @@ -0,0 +1,85 @@ +/* + * Copyright 2023 jacqueline <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include <deque> +#include <mutex> +#include <vector> + +#include "track.hpp" + +namespace audio { + +/* + * Owns and manages a complete view of the playback queue. Includes the + * currently playing track, a truncated list of previously played tracks, and + * all future tracks that have been queued. + * + * In order to not use all of our memory, this class deals strictly with track + * ids. Consumers that need more data than this should fetch it from the + * database. + * + * Instances of this class are broadly safe to use from multiple tasks; each + * method represents an atomic operation. No guarantees are made about + * consistency between calls however. For example, there may be data changes + * between consecutive calls to AddNext() and GetUpcoming(); + */ +class TrackQueue { + public: + TrackQueue(); + + /* Returns the currently playing track. */ + auto GetCurrent() const -> std::optional<database::TrackId>; + /* Returns, in order, tracks that have been queued to be played next. */ + auto GetUpcoming(std::size_t limit) const -> std::vector<database::TrackId>; + + /* + * Enqueues a track, placing it immediately after the current track and + * before anything already queued. + * + * If there is no current track, the given track will begin playback. + */ + auto AddNext(database::TrackId) -> void; + auto AddNext(const std::vector<database::TrackId>&) -> void; + + /* + * Enqueues a track, placing it the end of all enqueued tracks. + * + * If there is no current track, the given track will begin playback. + */ + auto AddLast(database::TrackId) -> void; + auto AddLast(const std::vector<database::TrackId>&) -> void; + + /* + * Advances to the next track in the queue, placing the current track at the + * front of the 'played' queue. + */ + auto Next() -> void; + auto Previous() -> void; + + /* + * Removes all tracks from all queues, and stops any currently playing track. + */ + auto Clear() -> void; + /* + * Removes a specific track from the queue of upcoming tracks. Has no effect + * on the currently playing track. + */ + auto RemoveUpcoming(database::TrackId) -> void; + + TrackQueue(const TrackQueue&) = delete; + TrackQueue& operator=(const TrackQueue&) = delete; + + private: + mutable std::mutex mutex_; + + std::deque<database::TrackId> played_; + std::deque<database::TrackId> upcoming_; + std::optional<database::TrackId> current_; +}; + +} // namespace audio |
