Audacity 3.2.0
CircularSampleBuffer.h
Go to the documentation of this file.
1#pragma once
2
3#include <cassert>
4
5#include "VectorOps.h"
6
7/*
8 * Utility buffer class for delay-based effects
9 * Allocates a 2^n size buffer for branchless write and read using a bitmask
10 * It can read and write to past and future samples, but does not check for overlaps
11 */
12
13namespace staffpad::audio {
14
15template <typename SampleT>
17{
18public:
20 {
21 }
22
24 {
25 if (_buffer)
27 }
28
29 void setSize(int n)
30 {
31 if (n > _allocatedSize)
32 {
33 auto oldSize = _allocatedSize;
34
35 auto findLargerPowerOfTwo = [](int32_t number) {
36 int32_t powerOf2 = 1;
37 while (powerOf2 < number)
38 powerOf2 *= 2;
39 return powerOf2;
40 };
41
42 _allocatedSize = findLargerPowerOfTwo(n);
44 _buffer = (SampleT*)std::realloc(_buffer, _allocatedSize * sizeof(SampleT));
45
46 // Reset the new memory region
47 assert(_buffer);
48 std::fill(_buffer + oldSize, _buffer + _allocatedSize, 0.f);
49 }
50 }
51
52 int getAllocatedSize() const
53 {
54 return _allocatedSize;
55 }
56
57 void reset()
58 {
59 if (_buffer && _allocatedSize > 0)
60 memset(_buffer, 0, sizeof(SampleT) * _allocatedSize);
61 _position0 = 0;
62 }
63
64 void write(int offset, const SampleT& sample)
65 {
66 _buffer[(_position0 + offset) & _bufferSizeMask] = sample;
67 }
68
69 void writeOffset0(const SampleT& sample)
70 {
71 _buffer[_position0] = sample;
72 }
73
74 const SampleT& read(int offset) const
75 {
76 return _buffer[(_position0 + offset) & _bufferSizeMask];
77 }
78
80 void advance(int n)
81 {
82 _position0 += n;
84 }
85
86private:
87 template <typename fnc>
88 void _splitBlockOffsetFunction(int startOffset, int n, fnc f) const
89 {
90 assert(n <= _allocatedSize);
91 int firstIndex = (_position0 + startOffset) & _bufferSizeMask;
92 int n_to_end = _allocatedSize - firstIndex;
93 if (n_to_end > n)
94 {
95 f(firstIndex, 0, n);
96 }
97 else
98 {
99 f(firstIndex, 0, n_to_end);
100 f(0, n_to_end, n - n_to_end);
101 }
102 }
103
104public:
105 void writeBlock(int startOffset, int n, const SampleT* sourceBlock)
106 {
107 _splitBlockOffsetFunction(startOffset, n, [=](int bufferOff, int sampleOff, int n) {
108 vo::copy(&sourceBlock[sampleOff], &_buffer[bufferOff], n);
109 });
110 }
111
112 void readBlockWithGain(int startOffset, int n, SampleT* targetBlock, float gainFactor) const
113 {
114 _splitBlockOffsetFunction(startOffset, n, [=](int bufferOff, int sampleOff, int n) {
115 vo::constantMultiply(&_buffer[bufferOff], gainFactor, &targetBlock[sampleOff], n);
116 });
117 }
118
119 void readAddBlockWithGain(int startOffset, int n, SampleT* targetBlock, float gainFactor) const
120 {
121 _splitBlockOffsetFunction(startOffset, n, [=](int bufferOff, int sampleOff, int n) {
122 vo::constantMultiplyAndAdd(&_buffer[bufferOff], gainFactor, &targetBlock[sampleOff], n);
123 });
124 }
125
126 void writeAddBlockWithGain(int startOffset, int n, const SampleT* sourceBlock, float gainFactor)
127 {
128 _splitBlockOffsetFunction(startOffset, n, [=](int bufferOff, int sampleOff, int n) {
129 vo::constantMultiplyAndAdd(&sourceBlock[sampleOff], gainFactor, &_buffer[bufferOff], n);
130 });
131 }
132
133 void readBlock(int startOffset, int n, SampleT* targetBlock) const
134 {
135 _splitBlockOffsetFunction(startOffset, n, [=](int bufferOff, int sampleOff, int n) {
136 vo::copy(&_buffer[bufferOff], &targetBlock[sampleOff], n);
137 });
138 }
139
140 void readAndClearBlock(int startOffset, int n, SampleT* targetBlock)
141 {
142 _splitBlockOffsetFunction(startOffset, n, [=](int bufferOff, int sampleOff, int n) {
143 vo::copy(&_buffer[bufferOff], &targetBlock[sampleOff], n);
144 vo::setToZero(&_buffer[bufferOff], n);
145 });
146 }
147
148 void clearBlock(int startOffset, int n)
149 {
150 _splitBlockOffsetFunction(startOffset, n,
151 [=](int bufferOff, int sampleOff, int n) { vo::setToZero(&_buffer[bufferOff], n); });
152 }
153
154private:
155 SampleT* _buffer = nullptr;
156
157 int _position0 = 0; // position of sample index 0 inside _buffer. This is where the next sample will be written to
158 int _allocatedSize = 0; // 2^n buffer size
159 int _bufferSizeMask = 0; // 2^n-1 buffer mask
160};
161
162} // namespace staffpad::audio
void readBlockWithGain(int startOffset, int n, SampleT *targetBlock, float gainFactor) const
const SampleT & read(int offset) const
void writeAddBlockWithGain(int startOffset, int n, const SampleT *sourceBlock, float gainFactor)
void writeOffset0(const SampleT &sample)
void write(int offset, const SampleT &sample)
void readAndClearBlock(int startOffset, int n, SampleT *targetBlock)
void writeBlock(int startOffset, int n, const SampleT *sourceBlock)
void readAddBlockWithGain(int startOffset, int n, SampleT *targetBlock, float gainFactor) const
void _splitBlockOffsetFunction(int startOffset, int n, fnc f) const
void readBlock(int startOffset, int n, SampleT *targetBlock) const
void clearBlock(int startOffset, int n)
void advance(int n)
change the 0 position by n
void setToZero(T *dst, int32_t n)
Definition: VectorOps.h:81
void free(void *ptr)
Definition: VectorOps.h:34
void constantMultiplyAndAdd(const T *src, T constant, T *dst, int32_t n)
Definition: VectorOps.h:67
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40
void constantMultiply(const T *src, T constant, T *dst, int32_t n)
Definition: VectorOps.h:60