VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / utils / CarlaRingBuffer.hpp
blob906005b2915cc0d69fa9b068965e8c25110cd170
1 /*
2 * Carla Ring Buffer
3 * Copyright (C) 2013-2023 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #ifndef CARLA_RING_BUFFER_HPP_INCLUDED
19 #define CARLA_RING_BUFFER_HPP_INCLUDED
21 #include "CarlaMathUtils.hpp"
22 #include "CarlaMemUtils.hpp"
24 // --------------------------------------------------------------------------------------------------------------------
25 // Buffer structs
28 head:
29 current writing position, headmost position of the buffer.
30 increments when writing.
32 tail:
33 current reading position, last used position of the buffer.
34 increments when reading.
35 head == tail means empty buffer
37 wrtn:
38 temporary position of head until a commitWrite() is called.
39 if buffer writing fails, wrtn will be back to head position thus ignoring the last operation(s).
40 if buffer writing succeeds, head will be set to this variable.
42 invalidateCommit:
43 boolean used to check if a write operation failed.
44 this ensures we don't get incomplete writes.
47 struct HeapBuffer {
48 uint32_t size;
49 uint32_t head, tail, wrtn;
50 bool invalidateCommit;
51 uint8_t* buf;
53 void copyDataFrom(const HeapBuffer& rb) noexcept
55 CARLA_SAFE_ASSERT_RETURN(size == rb.size,);
57 head = rb.head;
58 tail = rb.tail;
59 wrtn = rb.wrtn;
60 invalidateCommit = rb.invalidateCommit;
61 std::memcpy(buf, rb.buf, size);
65 struct SmallStackBuffer {
66 static constexpr const uint32_t size = 4096;
67 uint32_t head, tail, wrtn;
68 bool invalidateCommit;
69 uint8_t buf[size];
72 struct BigStackBuffer {
73 static constexpr const uint32_t size = 16384;
74 uint32_t head, tail, wrtn;
75 bool invalidateCommit;
76 uint8_t buf[size];
79 struct HugeStackBuffer {
80 static constexpr const uint32_t size = 65536;
81 uint32_t head, tail, wrtn;
82 bool invalidateCommit;
83 uint8_t buf[size];
86 // --------------------------------------------------------------------------------------------------------------------
87 // CarlaRingBufferControl templated class
89 template <class BufferStruct>
90 class CarlaRingBufferControl
92 public:
93 CarlaRingBufferControl() noexcept
94 : fBuffer(nullptr),
95 fErrorReading(false),
96 fErrorWriting(false) {}
98 virtual ~CarlaRingBufferControl() noexcept {}
100 // ----------------------------------------------------------------------------------------------------------------
102 void clearData() noexcept
104 CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
106 fBuffer->head = fBuffer->tail = fBuffer->wrtn = 0;
107 fBuffer->invalidateCommit = false;
109 carla_zeroBytes(fBuffer->buf, fBuffer->size);
111 fErrorReading = fErrorWriting = false;
114 void flush() noexcept
116 CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
118 fBuffer->head = fBuffer->tail = fBuffer->wrtn = 0;
119 fBuffer->invalidateCommit = false;
121 fErrorWriting = false;
124 // ----------------------------------------------------------------------------------------------------------------
126 bool commitWrite() noexcept
128 CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
130 if (fBuffer->invalidateCommit)
132 fBuffer->wrtn = fBuffer->head;
133 fBuffer->invalidateCommit = false;
134 return false;
137 // nothing to commit?
138 CARLA_SAFE_ASSERT_RETURN(fBuffer->head != fBuffer->wrtn, false);
140 // all ok
141 fBuffer->head = fBuffer->wrtn;
142 fErrorWriting = false;
143 return true;
146 bool isDataAvailableForReading() const noexcept;
148 bool isEmpty() const noexcept
150 CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
152 return fBuffer->buf == nullptr || fBuffer->head == fBuffer->tail;
155 uint32_t getSize() const noexcept
157 return fBuffer != nullptr ? fBuffer->size : 0;
160 uint32_t getReadableDataSize() const noexcept
162 CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, 0);
164 const uint32_t wrap = fBuffer->head >= fBuffer->tail ? 0 : fBuffer->size;
166 return wrap + fBuffer->head - fBuffer->tail;
169 uint32_t getWritableDataSize() const noexcept
171 CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, 0);
173 const uint32_t wrap = fBuffer->tail > fBuffer->wrtn ? 0 : fBuffer->size;
175 return wrap + fBuffer->tail - fBuffer->wrtn - 1;
178 // ----------------------------------------------------------------------------------------------------------------
180 bool readBool() noexcept
182 bool b = false;
183 return tryRead(&b, sizeof(bool)) ? b : false;
186 uint8_t readByte() noexcept
188 uint8_t B = 0;
189 return tryRead(&B, sizeof(uint8_t)) ? B : 0;
192 int16_t readShort() noexcept
194 int16_t s = 0;
195 return tryRead(&s, sizeof(int16_t)) ? s : 0;
198 uint16_t readUShort() noexcept
200 uint16_t us = 0;
201 return tryRead(&us, sizeof(uint16_t)) ? us : 0;
204 int32_t readInt() noexcept
206 int32_t i = 0;
207 return tryRead(&i, sizeof(int32_t)) ? i : 0;
210 uint32_t readUInt() noexcept
212 uint32_t ui = 0;
213 return tryRead(&ui, sizeof(int32_t)) ? ui : 0;
216 int64_t readLong() noexcept
218 int64_t l = 0;
219 return tryRead(&l, sizeof(int64_t)) ? l : 0;
222 uint64_t readULong() noexcept
224 uint64_t ul = 0;
225 return tryRead(&ul, sizeof(int64_t)) ? ul : 0;
228 float readFloat() noexcept
230 float f = 0.0f;
231 return tryRead(&f, sizeof(float)) ? f : 0.0f;
234 double readDouble() noexcept
236 double d = 0.0;
237 return tryRead(&d, sizeof(double)) ? d : 0.0;
240 void readCustomData(void* const data, const uint32_t size) noexcept
242 CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
243 CARLA_SAFE_ASSERT_RETURN(size > 0,);
245 if (! tryRead(data, size))
246 std::memset(data, 0, size);
249 template <typename T>
250 void readCustomType(T& type) noexcept
252 if (! tryRead(&type, sizeof(T)))
253 std::memset(&type, 0, sizeof(T));
256 // ----------------------------------------------------------------------------------------------------------------
258 void skipRead(const uint32_t size) noexcept
260 CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
261 CARLA_SAFE_ASSERT_RETURN(size > 0,);
262 CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size,);
264 // empty
265 if (fBuffer->head == fBuffer->tail)
266 return;
268 const uint32_t head = fBuffer->head;
269 const uint32_t tail = fBuffer->tail;
270 const uint32_t wrap = head > tail ? 0 : fBuffer->size;
272 if (size > wrap + head - tail)
274 if (! fErrorReading)
276 fErrorReading = true;
277 carla_stderr2("CarlaRingBuffer::skipRead(%u): failed, not enough space", size);
279 return;
282 uint32_t readto = tail + size;
284 if (readto >= fBuffer->size)
285 readto -= fBuffer->size;
287 fBuffer->tail = readto;
288 fErrorReading = false;
289 return;
292 // ----------------------------------------------------------------------------------------------------------------
294 bool writeBool(const bool value) noexcept
296 return tryWrite(&value, sizeof(bool));
299 bool writeByte(const uint8_t value) noexcept
301 return tryWrite(&value, sizeof(uint8_t));
304 bool writeShort(const int16_t value) noexcept
306 return tryWrite(&value, sizeof(int16_t));
309 bool writeUShort(const uint16_t value) noexcept
311 return tryWrite(&value, sizeof(uint16_t));
314 bool writeInt(const int32_t value) noexcept
316 return tryWrite(&value, sizeof(int32_t));
319 bool writeUInt(const uint32_t value) noexcept
321 return tryWrite(&value, sizeof(uint32_t));
324 bool writeLong(const int64_t value) noexcept
326 return tryWrite(&value, sizeof(int64_t));
329 bool writeULong(const uint64_t value) noexcept
331 return tryWrite(&value, sizeof(uint64_t));
334 bool writeFloat(const float value) noexcept
336 return tryWrite(&value, sizeof(float));
339 bool writeDouble(const double value) noexcept
341 return tryWrite(&value, sizeof(double));
344 bool writeCustomData(const void* const data, const uint32_t size) noexcept
346 CARLA_SAFE_ASSERT_RETURN(data != nullptr, false);
347 CARLA_SAFE_ASSERT_RETURN(size > 0, false);
349 return tryWrite(data, size);
352 template <typename T>
353 bool writeCustomType(const T& type) noexcept
355 return tryWrite(&type, sizeof(T));
358 // ----------------------------------------------------------------------------------------------------------------
360 protected:
361 void setRingBuffer(BufferStruct* const ringBuf, const bool resetBuffer) noexcept
363 CARLA_SAFE_ASSERT_RETURN(fBuffer != ringBuf,);
365 fBuffer = ringBuf;
367 if (resetBuffer && ringBuf != nullptr)
368 clearData();
371 // ----------------------------------------------------------------------------------------------------------------
373 bool tryRead(void* const buf, const uint32_t size) noexcept
375 CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
376 #if defined(__clang__)
377 #pragma clang diagnostic push
378 #pragma clang diagnostic ignored "-Wtautological-pointer-compare"
379 #endif
380 CARLA_SAFE_ASSERT_RETURN(fBuffer->buf != nullptr, false);
381 #if defined(__clang__)
382 #pragma clang diagnostic pop
383 #endif
384 CARLA_SAFE_ASSERT_RETURN(buf != nullptr, false);
385 CARLA_SAFE_ASSERT_RETURN(size > 0, false);
386 CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size, false);
388 // empty
389 if (fBuffer->head == fBuffer->tail)
390 return false;
392 uint8_t* const bytebuf = static_cast<uint8_t*>(buf);
394 const uint32_t head = fBuffer->head;
395 const uint32_t tail = fBuffer->tail;
396 const uint32_t wrap = head > tail ? 0 : fBuffer->size;
398 if (size > wrap + head - tail)
400 if (! fErrorReading)
402 fErrorReading = true;
403 carla_stderr2("CarlaRingBuffer::tryRead(%p, %u): failed, not enough space", buf, size);
405 return false;
408 uint32_t readto = tail + size;
410 if (readto > fBuffer->size)
412 readto -= fBuffer->size;
414 if (size == 1)
416 std::memcpy(bytebuf, fBuffer->buf + tail, 1);
418 else
420 const uint32_t firstpart = fBuffer->size - tail;
421 std::memcpy(bytebuf, fBuffer->buf + tail, firstpart);
422 std::memcpy(bytebuf + firstpart, fBuffer->buf, readto);
425 else
427 std::memcpy(bytebuf, fBuffer->buf + tail, size);
429 if (readto == fBuffer->size)
430 readto = 0;
433 fBuffer->tail = readto;
434 fErrorReading = false;
435 return true;
438 bool tryWrite(const void* const buf, const uint32_t size) noexcept
440 CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
441 CARLA_SAFE_ASSERT_RETURN(buf != nullptr, false);
442 CARLA_SAFE_ASSERT_RETURN(size > 0, false);
443 CARLA_SAFE_ASSERT_UINT2_RETURN(size < fBuffer->size, size, fBuffer->size, false);
445 const uint8_t* const bytebuf = static_cast<const uint8_t*>(buf);
447 const uint32_t tail = fBuffer->tail;
448 const uint32_t wrtn = fBuffer->wrtn;
449 const uint32_t wrap = tail > wrtn ? 0 : fBuffer->size;
451 if (size >= wrap + tail - wrtn)
453 if (! fErrorWriting)
455 fErrorWriting = true;
456 carla_stderr2("CarlaRingBuffer::tryWrite(%p, %u): failed, not enough space", buf, size);
458 fBuffer->invalidateCommit = true;
459 return false;
462 uint32_t writeto = wrtn + size;
464 if (writeto > fBuffer->size)
466 writeto -= fBuffer->size;
468 if (size == 1)
470 std::memcpy(fBuffer->buf, bytebuf, 1);
472 else
474 const uint32_t firstpart = fBuffer->size - wrtn;
475 std::memcpy(fBuffer->buf + wrtn, bytebuf, firstpart);
476 std::memcpy(fBuffer->buf, bytebuf + firstpart, writeto);
479 else
481 std::memcpy(fBuffer->buf + wrtn, bytebuf, size);
483 if (writeto == fBuffer->size)
484 writeto = 0;
487 fBuffer->wrtn = writeto;
488 return true;
491 private:
492 BufferStruct* fBuffer;
494 // wherever read/write errors have been printed to terminal
495 bool fErrorReading;
496 bool fErrorWriting;
498 CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
499 CARLA_DECLARE_NON_COPYABLE(CarlaRingBufferControl)
502 template <class BufferStruct>
503 inline bool CarlaRingBufferControl<BufferStruct>::isDataAvailableForReading() const noexcept
505 return (fBuffer != nullptr && fBuffer->head != fBuffer->tail);
508 template <>
509 inline bool CarlaRingBufferControl<HeapBuffer>::isDataAvailableForReading() const noexcept
511 return (fBuffer != nullptr && fBuffer->buf != nullptr && fBuffer->head != fBuffer->tail);
514 // --------------------------------------------------------------------------------------------------------------------
515 // CarlaRingBuffer using heap space
517 class CarlaHeapRingBuffer : public CarlaRingBufferControl<HeapBuffer>
519 public:
520 CarlaHeapRingBuffer() noexcept
521 : fHeapBuffer{0, 0, 0, 0, false, nullptr} {}
523 ~CarlaHeapRingBuffer() noexcept override
525 if (fHeapBuffer.buf == nullptr)
526 return;
528 delete[] fHeapBuffer.buf;
529 fHeapBuffer.buf = nullptr;
532 void createBuffer(const uint32_t size, const bool mlock) noexcept
534 CARLA_SAFE_ASSERT_RETURN(fHeapBuffer.buf == nullptr,);
535 CARLA_SAFE_ASSERT_RETURN(size > 0,);
537 const uint32_t p2size = carla_nextPowerOf2(size);
539 try {
540 fHeapBuffer.buf = new uint8_t[p2size];
541 } CARLA_SAFE_EXCEPTION_RETURN("CarlaHeapRingBuffer::createBuffer",);
543 fHeapBuffer.size = p2size;
544 setRingBuffer(&fHeapBuffer, true);
546 if (mlock)
548 carla_mlock(&fHeapBuffer, sizeof(fHeapBuffer));
549 carla_mlock(fHeapBuffer.buf, p2size);
553 void deleteBuffer() noexcept
555 if (fHeapBuffer.buf == nullptr)
556 return;
558 setRingBuffer(nullptr, false);
560 delete[] fHeapBuffer.buf;
561 fHeapBuffer.buf = nullptr;
562 fHeapBuffer.size = 0;
565 private:
566 HeapBuffer fHeapBuffer;
568 CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
569 CARLA_DECLARE_NON_COPYABLE(CarlaHeapRingBuffer)
572 // --------------------------------------------------------------------------------------------------------------------
573 // CarlaRingBuffer using small stack space
575 class CarlaSmallStackRingBuffer : public CarlaRingBufferControl<SmallStackBuffer>
577 public:
578 CarlaSmallStackRingBuffer() noexcept
579 : fStackBuffer{0, 0, 0, false, {}}
581 setRingBuffer(&fStackBuffer, true);
584 private:
585 SmallStackBuffer fStackBuffer;
587 CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
588 CARLA_DECLARE_NON_COPYABLE(CarlaSmallStackRingBuffer)
591 // --------------------------------------------------------------------------------------------------------------------
593 #endif // CARLA_RING_BUFFER_HPP_INCLUDED