From f84474d94d3618b9dc2581b72aea768052a40dd7 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Fri, 31 May 2024 11:52:19 +1000 Subject: Introduce a PcmBuffer abstraction for handling source draining --- src/drivers/include/drivers/bluetooth.hpp | 10 ++--- src/drivers/include/drivers/i2s_dac.hpp | 11 +++-- src/drivers/include/drivers/pcm_buffer.hpp | 72 ++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 src/drivers/include/drivers/pcm_buffer.hpp (limited to 'src/drivers/include') diff --git a/src/drivers/include/drivers/bluetooth.hpp b/src/drivers/include/drivers/bluetooth.hpp index ad61fcc1..030907d9 100644 --- a/src/drivers/include/drivers/bluetooth.hpp +++ b/src/drivers/include/drivers/bluetooth.hpp @@ -14,6 +14,7 @@ #include #include "drivers/bluetooth_types.hpp" #include "drivers/nvs.hpp" +#include "drivers/pcm_buffer.hpp" #include "esp_a2dp_api.h" #include "esp_avrc_api.h" #include "esp_gap_bt_api.h" @@ -42,9 +43,8 @@ class Bluetooth { auto SetPreferredDevice(std::optional dev) -> void; auto PreferredDevice() -> std::optional; - auto SetSource(StreamBufferHandle_t) -> void; + auto SetSource(PcmBuffer*) -> void; auto SetVolumeFactor(float) -> void; - auto SamplesUsed() -> uint32_t; auto SetEventHandler(std::function cb) -> void; }; @@ -114,8 +114,8 @@ class BluetoothState : public tinyfsm::Fsm { static auto discovery() -> bool; static auto discovery(bool) -> void; - static auto source() -> StreamBufferHandle_t; - static auto source(StreamBufferHandle_t) -> void; + static auto source() -> PcmBuffer*; + static auto source(PcmBuffer*) -> void; static auto event_handler(std::function) -> void; @@ -147,7 +147,7 @@ class BluetoothState : public tinyfsm::Fsm { static std::optional sConnectingDevice_; static int sConnectAttemptsRemaining_; - static std::atomic sSource_; + static std::atomic sSource_; static std::function sEventHandler_; auto connect(const bluetooth::MacAndName&) -> bool; diff --git a/src/drivers/include/drivers/i2s_dac.hpp b/src/drivers/include/drivers/i2s_dac.hpp index 0776dbab..138a0c03 100644 --- a/src/drivers/include/drivers/i2s_dac.hpp +++ b/src/drivers/include/drivers/i2s_dac.hpp @@ -16,6 +16,7 @@ #include "driver/i2s_std.h" #include "driver/i2s_types.h" +#include "drivers/pcm_buffer.hpp" #include "esp_err.h" #include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" @@ -39,9 +40,9 @@ constexpr size_t kI2SBufferLengthFrames = 1024; */ class I2SDac { public: - static auto create(IGpios& expander) -> std::optional; + static auto create(IGpios& expander, PcmBuffer&) -> std::optional; - I2SDac(IGpios& gpio, i2s_chan_handle_t i2s_handle); + I2SDac(IGpios& gpio, PcmBuffer&, i2s_chan_handle_t i2s_handle); ~I2SDac(); auto Start() -> void; @@ -69,9 +70,6 @@ class I2SDac { auto Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate) -> void; auto WriteData(const std::span& data) -> void; - auto SetSource(StreamBufferHandle_t buffer) -> void; - - auto SamplesUsed() -> uint32_t; // Not copyable or movable. I2SDac(const I2SDac&) = delete; @@ -81,9 +79,10 @@ class I2SDac { auto set_channel(bool) -> void; IGpios& gpio_; + PcmBuffer& buffer_; i2s_chan_handle_t i2s_handle_; + bool i2s_active_; - StreamBufferHandle_t buffer_; std::mutex configure_mutex_; i2s_std_clk_config_t clock_config_; diff --git a/src/drivers/include/drivers/pcm_buffer.hpp b/src/drivers/include/drivers/pcm_buffer.hpp new file mode 100644 index 00000000..6630f720 --- /dev/null +++ b/src/drivers/include/drivers/pcm_buffer.hpp @@ -0,0 +1,72 @@ +/* + * Copyright 2024 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include +#include +#include + +#include "freertos/FreeRTOS.h" + +#include "freertos/ringbuf.h" +#include "portmacro.h" + +namespace drivers { + +/* + * A circular buffer of signed, 16-bit PCM samples. PcmBuffers are the main + * data structure used for shuffling large amounts of read-to-play samples + * throughout the system. + */ +class PcmBuffer { + public: + PcmBuffer(size_t size_in_samples); + ~PcmBuffer(); + + /* Adds samples to the buffer. */ + auto send(std::span) -> void; + + /* + * Fills the given span with samples. If enough samples are available in + * the buffer, then the span will be filled with samples from the buffer. Any + * shortfall is made up by padding the given span with zeroes. + */ + auto receive(std::span, bool isr) -> BaseType_t; + + auto clear() -> void; + auto isEmpty() -> bool; + + /* + * How many samples have been added to this buffer since it was created. This + * method overflows by wrapping around to zero. + */ + auto totalSent() -> uint32_t; + + /* + * How many samples have been removed from this buffer since it was created. + * This method overflows by wrapping around to zero. + */ + auto totalReceived() -> uint32_t; + + // Not copyable or movable. + PcmBuffer(const PcmBuffer&) = delete; + PcmBuffer& operator=(const PcmBuffer&) = delete; + + private: + auto readSingle(std::span, bool isr) + -> std::pair; + + StaticRingbuffer_t meta_; + uint8_t* buf_; + + std::atomic sent_; + std::atomic received_; + RingbufHandle_t ringbuf_; +}; + +} // namespace drivers -- cgit v1.2.3