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/pcm_buffer.cpp | 118 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/drivers/pcm_buffer.cpp (limited to 'src/drivers/pcm_buffer.cpp') diff --git a/src/drivers/pcm_buffer.cpp b/src/drivers/pcm_buffer.cpp new file mode 100644 index 00000000..3f4a0443 --- /dev/null +++ b/src/drivers/pcm_buffer.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2024 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "drivers/pcm_buffer.hpp" +#include + +#include +#include +#include +#include +#include + +#include "esp_log.h" +#include "freertos/FreeRTOS.h" + +#include "esp_heap_caps.h" +#include "freertos/ringbuf.h" +#include "portmacro.h" + +namespace drivers { + +[[maybe_unused]] static const char kTag[] = "pcmbuf"; + +PcmBuffer::PcmBuffer(size_t size_in_samples) : sent_(0), received_(0) { + size_t size_in_bytes = size_in_samples * sizeof(int16_t); + ESP_LOGI(kTag, "allocating pcm buffer of size %u (%uKiB)", size_in_samples, + size_in_bytes / 1024); + buf_ = reinterpret_cast( + heap_caps_malloc(size_in_bytes, MALLOC_CAP_SPIRAM)); + ringbuf_ = xRingbufferCreateStatic(size_in_bytes, RINGBUF_TYPE_BYTEBUF, buf_, + &meta_); +} + +PcmBuffer::~PcmBuffer() { + vRingbufferDelete(ringbuf_); + heap_caps_free(buf_); +} + +auto PcmBuffer::send(std::span data) -> void { + xRingbufferSend(ringbuf_, data.data(), data.size_bytes(), portMAX_DELAY); + sent_ += data.size(); +} + +IRAM_ATTR auto PcmBuffer::receive(std::span dest, bool isr) + -> BaseType_t { + size_t first_read = 0, second_read = 0; + BaseType_t ret1 = false, ret2 = false; + std::tie(first_read, ret1) = readSingle(dest, isr); + + if (first_read < dest.size()) { + std::tie(second_read, ret2) = readSingle(dest.subspan(first_read), isr); + } + + size_t total_read = first_read + second_read; + if (total_read < dest.size()) { + std::fill_n(dest.begin() + total_read, dest.size() - total_read, 0); + } + + received_ += first_read + second_read; + + return ret1 || ret2; +} + +auto PcmBuffer::clear() -> void { + while (!isEmpty()) { + size_t bytes_cleared; + void* data = xRingbufferReceive(ringbuf_, &bytes_cleared, 0); + vRingbufferReturnItem(ringbuf_, data); + received_ += bytes_cleared / sizeof(int16_t); + } +} + +auto PcmBuffer::isEmpty() -> bool { + return xRingbufferGetMaxItemSize(ringbuf_) == + xRingbufferGetCurFreeSize(ringbuf_); +} + +auto PcmBuffer::totalSent() -> uint32_t { + return sent_; +} + +auto PcmBuffer::totalReceived() -> uint32_t { + return received_; +} + +IRAM_ATTR auto PcmBuffer::readSingle(std::span dest, bool isr) + -> std::pair { + BaseType_t ret; + size_t read_bytes = 0; + void* data; + if (isr) { + data = + xRingbufferReceiveUpToFromISR(ringbuf_, &read_bytes, dest.size_bytes()); + } else { + data = xRingbufferReceiveUpTo(ringbuf_, &read_bytes, 0, dest.size_bytes()); + } + + size_t read_samples = read_bytes / sizeof(int16_t); + + if (!data) { + return {read_samples, ret}; + } + + std::memcpy(dest.data(), data, read_bytes); + + if (isr) { + vRingbufferReturnItem(ringbuf_, data); + } else { + vRingbufferReturnItemFromISR(ringbuf_, data, &ret); + } + + return {read_samples, ret}; +} + +} // namespace drivers -- cgit v1.2.3