1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
/*
* 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 = 0;
void* data = xRingbufferReceive(ringbuf_, &bytes_cleared, 0);
if (data) {
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
|