diff options
| author | jacqueline <me@jacqueline.id.au> | 2024-05-31 11:52:19 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2024-05-31 11:52:19 +1000 |
| commit | f84474d94d3618b9dc2581b72aea768052a40dd7 (patch) | |
| tree | 85aa535684074acf09fb7e19dd21d03edbe94443 /src/drivers/pcm_buffer.cpp | |
| parent | 2ff8eac022f397bb1aed28aca376fbe422fc8b3c (diff) | |
| download | tangara-fw-f84474d94d3618b9dc2581b72aea768052a40dd7.tar.gz | |
Introduce a PcmBuffer abstraction for handling source draining
Diffstat (limited to 'src/drivers/pcm_buffer.cpp')
| -rw-r--r-- | src/drivers/pcm_buffer.cpp | 118 |
1 files changed, 118 insertions, 0 deletions
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 <me@jacqueline.id.au> + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "drivers/pcm_buffer.hpp" +#include <stdint.h> + +#include <algorithm> +#include <cstddef> +#include <cstring> +#include <span> +#include <tuple> + +#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<uint8_t*>( + 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<const int16_t> data) -> void { + xRingbufferSend(ringbuf_, data.data(), data.size_bytes(), portMAX_DELAY); + sent_ += data.size(); +} + +IRAM_ATTR auto PcmBuffer::receive(std::span<int16_t> 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<int16_t> dest, bool isr) + -> std::pair<size_t, BaseType_t> { + 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 |
