diff options
Diffstat (limited to 'src/audio/include')
| -rw-r--r-- | src/audio/include/audio_backend.hpp | 20 | ||||
| -rw-r--r-- | src/audio/include/audio_decoder.hpp | 34 | ||||
| -rw-r--r-- | src/audio/include/audio_element.hpp | 73 | ||||
| -rw-r--r-- | src/audio/include/audio_output.hpp | 29 | ||||
| -rw-r--r-- | src/audio/include/audio_playback.hpp | 97 | ||||
| -rw-r--r-- | src/audio/include/audio_task.hpp | 15 | ||||
| -rw-r--r-- | src/audio/include/fatfs_audio_input.hpp | 49 |
7 files changed, 317 insertions, 0 deletions
diff --git a/src/audio/include/audio_backend.hpp b/src/audio/include/audio_backend.hpp new file mode 100644 index 00000000..85985cc2 --- /dev/null +++ b/src/audio/include/audio_backend.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <cstdint> +namespace drivers { + +class IAudioBackend { + public: + virtual ~IAudioBackend() {} + + enum SampleRate {}; + enum BitDepth {}; + + virtual auto Configure(SampleRate sample_rate, BitDepth bit_depth) + -> bool = 0; + virtual auto WritePcmData(uint8_t* data, size_t length) -> bool = 0; + + virtual auto SetVolume(uint8_t percent) -> void = 0; +}; + +} // namespace drivers diff --git a/src/audio/include/audio_decoder.hpp b/src/audio/include/audio_decoder.hpp new file mode 100644 index 00000000..f460f9e9 --- /dev/null +++ b/src/audio/include/audio_decoder.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include <cstddef> +#include "ff.h" + +namespace audio { + +enum SampleRate {}; +enum BitDepth {}; + +struct PcmStreamHeader { + SampleRate sample_rate; + BitDepth bit_depth; + bool configure_now; +}; + +class AudioDecoder { + public: + AudioDecoder(); + ~AudioDecoder(); + + auto SetSource(RingbufHandle_t& source) -> void; + + enum Status {}; + auto ProcessChunk() -> Status; + + auto GetOutputStream() const -> RingbufHandle_t; + + private: + RingbufHandle_t input_; + RingbufHandle_t output_; +}; + +} // namespace audio diff --git a/src/audio/include/audio_element.hpp b/src/audio/include/audio_element.hpp new file mode 100644 index 00000000..ea4256ac --- /dev/null +++ b/src/audio/include/audio_element.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include <cstdint> + +namespace audio { + +extern const std::size_t kMaxFrameSize; + +class IAudioElement { + public: + virtual ~IAudioElement(); + + enum CommandType { + /* + * Sets the sequence number of the most recent byte stream. Any commands + * received that have a lower sequence number than this will be discarded. + */ + SEQUENCE_NUMBER, + /* + * Instructs this element to read a specific number of bytes from its + * input buffer. + */ + READ_FRAME, + /* + * Represents an element-specific command. This handling of this is + * delegated to element implementations. + */ + ELEMENT, + /* Instructs this element to shut down. */ + QUIT, + }; + + struct Command { + CommandType type; + uint8_t sequence_number; + union { + void* data; + std::size_t frame_size; + }; + }; + + /* + * Returns a queue that should be used for all communication with this + * element. + */ + virtual auto InputCommandQueue() -> QueueHandle_t = 0; + + /* + * Returns a buffer that will be used to stream input bytes to this element. + * This may be NULL, if this element represents a source, e.g. a FATFS + * reader. + */ + virtual auto InputBuffer() -> StreamBufferHandle_t = 0; + + /* + * Called when an element-specific command has been received. + */ + virtual auto ProcessElementCommand(void* command) -> void = 0; + + virtual auto SkipElementCommand(void* command) -> void = 0; + + /* + * Called with the result of a read bytes command. + */ + virtual auto ProcessData(uint8_t* data, uint16_t length) -> void = 0; + + /* + * Called periodically when there are no pending commands. + */ + virtual auto ProcessIdle() -> void = 0; +}; + +} // namespace audio diff --git a/src/audio/include/audio_output.hpp b/src/audio/include/audio_output.hpp new file mode 100644 index 00000000..82dca82d --- /dev/null +++ b/src/audio/include/audio_output.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include <cstdint> +#include <memory> + +#include "audio_common.h" +#include "audio_element.h" + +namespace drivers { + +class IAudioOutput { + public: + IAudioOutput(audio_element_handle_t element) : element_(element) {} + virtual ~IAudioOutput() { audio_element_deinit(element_); } + + auto GetAudioElement() -> audio_element_handle_t { return element_; } + + virtual auto SetVolume(uint8_t volume) -> void = 0; + virtual auto GetVolume() const -> uint8_t { return volume_; } + + virtual auto Configure(audio_element_info_t& info) -> void = 0; + virtual auto SetSoftMute(bool enabled) -> void = 0; + + protected: + audio_element_handle_t element_; + uint8_t volume_; +}; + +} // namespace drivers diff --git a/src/audio/include/audio_playback.hpp b/src/audio/include/audio_playback.hpp new file mode 100644 index 00000000..41ab46d2 --- /dev/null +++ b/src/audio/include/audio_playback.hpp @@ -0,0 +1,97 @@ +#pragma once + +#include <cstdint> +#include <memory> +#include <string> + +#include "audio_common.h" +#include "audio_element.h" +#include "audio_event_iface.h" +#include "audio_pipeline.h" +#include "esp_err.h" +#include "fatfs_stream.h" +#include "i2s_stream.h" +#include "mp3_decoder.h" +#include "result.hpp" + +#include "audio_output.hpp" +#include "dac.hpp" +#include "storage.hpp" + +namespace drivers { + +/* + * Sends an I2S audio stream to the DAC. Includes basic controls for pausing + * and resuming the stream, as well as support for gapless playback of the next + * queued song, but does not implement any kind of sophisticated queing or + * playback control; these should be handled at a higher level. + */ +class AudioPlayback { + public: + enum Error { FATFS_INIT, I2S_INIT, PIPELINE_INIT }; + static auto create(std::unique_ptr<IAudioOutput> output) + -> cpp::result<std::unique_ptr<AudioPlayback>, Error>; + + AudioPlayback(std::unique_ptr<IAudioOutput>& output, + audio_pipeline_handle_t pipeline, + audio_element_handle_t source_element, + audio_event_iface_handle_t event_interface); + ~AudioPlayback(); + + /* + * Replaces any currently playing file with the one given, and begins + * playback. + * + * Any value set in `set_next_file` is cleared by this method. + */ + auto Play(const std::string& filename) -> void; + /* Toogle between resumed and paused. */ + auto Toggle() -> void; + auto Resume() -> void; + auto Pause() -> void; + + enum PlaybackState { PLAYING, PAUSED, STOPPED }; + auto GetPlaybackState() const -> PlaybackState; + + /* + * Handles any pending events from the underlying audio pipeline. This must + * be called regularly in order to handle configuring the I2S stream for + * different audio types (e.g. sample rate, bit depth), and for gapless + * playback. + */ + auto ProcessEvents(uint16_t max_time_ms) -> void; + + /* + * Sets the file that should be played immediately after the current file + * finishes. This is used for gapless playback + */ + auto SetNextFile(const std::string& filename) -> void; + + auto SetVolume(uint8_t volume) -> void; + auto GetVolume() const -> uint8_t; + + // Not copyable or movable. + AudioPlayback(const AudioPlayback&) = delete; + AudioPlayback& operator=(const AudioPlayback&) = delete; + + private: + PlaybackState playback_state_; + + enum Decoder { NONE, MP3, AMR, OPUS, OGG, FLAC, WAV, AAC }; + auto GetDecoderForFilename(std::string filename) const -> Decoder; + auto CreateDecoder(Decoder decoder) const -> audio_element_handle_t; + auto ReconfigurePipeline(Decoder decoder) -> void; + + std::unique_ptr<IAudioOutput> output_; + + std::string next_filename_ = ""; + + audio_pipeline_handle_t pipeline_; + audio_element_handle_t source_element_; + audio_event_iface_handle_t event_interface_; + + audio_element_handle_t decoder_ = nullptr; + Decoder decoder_type_ = NONE; +}; + +} // namespace drivers diff --git a/src/audio/include/audio_task.hpp b/src/audio/include/audio_task.hpp new file mode 100644 index 00000000..79604f33 --- /dev/null +++ b/src/audio/include/audio_task.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include <memory> + +#include "audio_element.hpp" + +namespace audio { + +struct AudioTaskArgs { + std::shared_ptr<IAudioElement>& element; +}; + +void audio_task(void* args); + +} // namespace audio diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp new file mode 100644 index 00000000..ed4da55e --- /dev/null +++ b/src/audio/include/fatfs_audio_input.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include <cstdint> +#include <memory> + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/stream_buffer.h" + +#include "audio_element.hpp" +#include "storage.hpp" + +namespace audio { + +class FatfsAudioInput : public IAudioElement { + public: + struct InputCommand { + std::string filename; + }; + + struct OutputCommand { + // TODO: does this actually need any special output? + }; + + FatfsAudioInput(std::shared_ptr<drivers::SdStorage> storage); + ~FatfsAudioInput(); + + auto OutputCommandQueue() -> QueueHandle_t; + auto OutputBuffer() -> StreamBufferHandle_t; + + private: + std::shared_ptr<drivers::SdStorage> storage_; + + uint8_t current_sequence = 0; + + uint8_t* input_queue_memory_; + StaticQueue_t input_queue_metadata_; + QueueHandle_t input_queue_; + + uint8_t* output_queue_memory_; + StaticQueue_t output_queue_metadata_; + QueueHandle_t output_queue_; + + uint8_t* output_buffer_memory_; + StaticStreamBuffer_t output_buffer_metadata_; + StreamBufferHandle_t output_buffer_; +}; + +} // namespace audio |
