arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
DynamicMultiBuffer.h
1#pragma once
2
3namespace audio_tools {
4
25template <typename T, template<typename> class BufferType>
26class DynamicMultiBuffer : public BaseBuffer<T> {
27 public:
35 DynamicMultiBuffer(size_t component_size, size_t initial_components = 1,
36 size_t max_components = 0) {
37 TRACED();
38 component_size_ = component_size;
39 max_components_ = max_components;
40
41 // Create initial buffer components
42 for (size_t i = 0; i < initial_components; i++) {
43 if (!addBufferComponent()) {
44 LOGE("Failed to allocate initial buffer component %d", (int)i);
45 break;
46 }
47 }
48 }
49
54 TRACED();
55 // Clean up all buffer components
56 for (auto* buffer : buffer_components) {
57 delete buffer;
58 }
59 buffer_components.clear();
60 }
61
70 bool read(T &result) override {
71 if (isEmpty()) {
72 return false;
73 }
74
75 // Find the buffer component containing the read position
76 size_t buffer_idx = read_pos / component_size_;
77 size_t local_pos = read_pos % component_size_;
78
79 // Perform read from the appropriate buffer
80 bool success = buffer_components[buffer_idx]->read(result);
81 if (success) {
82 read_pos++;
83 }
84
85 return success;
86 }
87
96 bool peek(T &result) override {
97 if (isEmpty()) {
98 return false;
99 }
100
101 // Find the buffer component containing the read position
102 size_t buffer_idx = read_pos / component_size_;
103 size_t local_pos = read_pos % component_size_;
104
105 // Perform peek from the appropriate buffer
106 return buffer_components[buffer_idx]->peek(result);
107 }
108
118 bool write(T data) override {
119 // Check if we need to add a new buffer component
120 if (isCurrentBufferFull()) {
121 if (!addBufferComponent()) {
122 return false; // Couldn't expand, so write fails
123 }
124 }
125
126 // Find the buffer component for writing
127 size_t buffer_idx = write_pos / component_size_;
128 size_t local_pos = write_pos % component_size_;
129
130 // Perform write to the appropriate buffer
131 bool success = buffer_components[buffer_idx]->write(data);
132 if (success) {
133 write_pos++;
134 }
135
136 return success;
137 }
138
148 int readArray(T data[], int len) override {
149 if (isEmpty() || data == nullptr) {
150 return 0;
151 }
152
153 int total_read = 0;
154 int remaining = min(len, available());
155
156 while (remaining > 0) {
157 // Find the current buffer component and position
158 size_t buffer_idx = read_pos / component_size_;
159 size_t local_pos = read_pos % component_size_;
160
161 // Calculate how many elements we can read from this component
162 int can_read = min(remaining, (int)(component_size_ - local_pos));
163
164 // Set up the buffer component for reading from the correct position
165 buffer_components[buffer_idx]->reset();
166 for (size_t i = 0; i < local_pos; i++) {
167 T dummy;
168 buffer_components[buffer_idx]->read(dummy);
169 }
170
171 // Read from the buffer component
172 int actually_read = buffer_components[buffer_idx]->readArray(data + total_read, can_read);
173
174 // Update positions
175 total_read += actually_read;
176 read_pos += actually_read;
177 remaining -= actually_read;
178
179 // If we couldn't read as much as expected, stop
180 if (actually_read < can_read) {
181 break;
182 }
183 }
184
185 return total_read;
186 }
187
198 int writeArray(const T data[], int len) override {
199 if (data == nullptr) {
200 return 0;
201 }
202
203 int total_written = 0;
204 int remaining = len;
205
206 while (remaining > 0) {
207 // Check if we need to add a new buffer component
208 if (isCurrentBufferFull()) {
209 if (!addBufferComponent()) {
210 break; // Couldn't expand, so stop writing
211 }
212 }
213
214 // Find the current buffer component and position
215 size_t buffer_idx = write_pos / component_size_;
216 size_t local_pos = write_pos % component_size_;
217
218 // Calculate how many elements we can write to this component
219 int can_write = min(remaining, (int)(component_size_ - local_pos));
220
221 // Set up the buffer component for writing at the correct position
222 buffer_components[buffer_idx]->reset();
223 for (size_t i = 0; i < local_pos; i++) {
224 buffer_components[buffer_idx]->write(T()); // Write dummy values
225 }
226
227 // Write to the buffer component
228 int actually_written = buffer_components[buffer_idx]->writeArray(data + total_written, can_write);
229
230 // Update positions
231 total_written += actually_written;
232 write_pos += actually_written;
233 remaining -= actually_written;
234
235 // If we couldn't write as much as expected, stop
236 if (actually_written < can_write) {
237 break;
238 }
239 }
240
241 return total_written;
242 }
243
249 void reset() override {
250 read_pos = 0;
251 write_pos = 0;
252
253 // Reset all buffer components
254 for (auto* buffer : buffer_components) {
255 buffer->reset();
256 }
257 }
258
264 int available() override {
265 return write_pos - read_pos;
266 }
267
276 int availableForWrite() override {
277 // Calculate space in existing buffers
278 size_t existing_space = totalCapacity() - write_pos;
279
280 // If no maximum component limit, return a large value
281 if (max_components_ == 0 || buffer_components.size() < max_components_) {
282 return existing_space + 1000000; // Effectively unlimited
283 }
284
285 return existing_space;
286 }
287
296 bool isFull() override {
297 if (max_components_ == 0) {
298 return false; // Never full if unlimited components
299 }
300
301 return buffer_components.size() >= max_components_ && isCurrentBufferFull();
302 }
303
309 bool isEmpty() override {
310 return available() == 0;
311 }
312
321 T* address() override {
322 if (isEmpty() || buffer_components.empty()) {
323 return nullptr;
324 }
325
326 size_t buffer_idx = read_pos / component_size_;
327 return buffer_components[buffer_idx]->address();
328 }
329
335 size_t size() override {
336 return totalCapacity();
337 }
338
347 bool resize(int new_size) override {
348 // Calculate needed components
349 size_t needed_components = (new_size + component_size_ - 1) / component_size_;
350
351 // Check if we're allowed to have this many components
352 if (max_components_ != 0 && needed_components > max_components_) {
353 needed_components = max_components_;
354 }
355
356 // Remove excess components
357 while (buffer_components.size() > needed_components) {
358 delete buffer_components.back();
359 buffer_components.pop_back();
360 }
361
362 // Add needed components
363 while (buffer_components.size() < needed_components) {
364 if (!addBufferComponent()) {
365 return false;
366 }
367 }
368
369 // Adjust read/write positions if they're now out of bounds
370 size_t total_capacity = totalCapacity();
371 if (write_pos > total_capacity) {
372 write_pos = total_capacity;
373 }
374 if (read_pos > write_pos) {
375 read_pos = write_pos;
376 }
377
378 return true;
379 }
380
387 return buffer_components.size();
388 }
389
396 return component_size_;
397 }
398
399 protected:
400 Vector<BufferType<T>*> buffer_components; // Collection of buffer components
401 size_t component_size_ = 0; // Size of each component in elements
402 size_t max_components_ = 0; // Maximum number of components (0 = unlimited)
403 size_t read_pos = 0; // Global read position
404 size_t write_pos = 0; // Global write position
405
414 // Check if we're allowed to add more components
415 if (max_components_ != 0 && buffer_components.size() >= max_components_) {
416 LOGW("Maximum number of buffer components reached: %d", (int)max_components_);
417 return false;
418 }
419
420 // Create new buffer component
421 BufferType<T>* new_buffer = new BufferType<T>(component_size_);
422 if (new_buffer == nullptr) {
423 LOGE("Failed to allocate new buffer component");
424 return false;
425 }
426
427 // make sure the component has the correct size
428 new_buffer->resize(component_size_);
429
430 // Add to our collection
431 buffer_components.push_back(new_buffer);
432 LOGI("Added buffer component #%d", (int)buffer_components.size());
433
434 return true;
435 }
436
442 size_t totalCapacity() {
443 return buffer_components.size() * component_size_;
444 }
445
452 if (buffer_components.empty()) {
453 return true;
454 }
455
456 size_t buffer_idx = write_pos / component_size_;
457 size_t local_pos = write_pos % component_size_;
458
459 return local_pos >= component_size_ || buffer_idx >= buffer_components.size();
460 }
461};
462
463}
Shared functionality of all buffers.
Definition Buffers.h:22
Auto-expanding buffer composed of multiple buffer instances.
Definition DynamicMultiBuffer.h:26
bool resize(int new_size) override
Resize the buffer.
Definition DynamicMultiBuffer.h:347
size_t getComponentCount()
Get the number of buffer components.
Definition DynamicMultiBuffer.h:386
size_t size() override
Get total capacity of the buffer.
Definition DynamicMultiBuffer.h:335
bool peek(T &result) override
Peek at the next value without removing it.
Definition DynamicMultiBuffer.h:96
bool read(T &result) override
Read a single value from the buffer.
Definition DynamicMultiBuffer.h:70
bool addBufferComponent()
Add a new buffer component.
Definition DynamicMultiBuffer.h:413
bool write(T data) override
Write a value to the buffer.
Definition DynamicMultiBuffer.h:118
DynamicMultiBuffer(size_t component_size, size_t initial_components=1, size_t max_components=0)
Constructor with buffer configuration.
Definition DynamicMultiBuffer.h:35
int available() override
Get number of elements available to read.
Definition DynamicMultiBuffer.h:264
virtual ~DynamicMultiBuffer()
Destructor - releases all buffer components.
Definition DynamicMultiBuffer.h:53
int availableForWrite() override
Get space available for writing.
Definition DynamicMultiBuffer.h:276
T * address() override
Get pointer to current read position.
Definition DynamicMultiBuffer.h:321
bool isFull() override
Check if the buffer is full.
Definition DynamicMultiBuffer.h:296
int writeArray(const T data[], int len) override
Optimized bulk write operation.
Definition DynamicMultiBuffer.h:198
bool isCurrentBufferFull()
Check if the current write buffer is full.
Definition DynamicMultiBuffer.h:451
size_t totalCapacity()
Calculate total capacity of all buffer components.
Definition DynamicMultiBuffer.h:442
size_t getComponentSize()
Get the size of each component.
Definition DynamicMultiBuffer.h:395
void reset() override
Reset the buffer to empty state.
Definition DynamicMultiBuffer.h:249
bool isEmpty() override
Check if the buffer is empty.
Definition DynamicMultiBuffer.h:309
int readArray(T data[], int len) override
Optimized bulk read operation.
Definition DynamicMultiBuffer.h:148
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10