Skip to content

Commit 267b96e

Browse files
authored
Merge pull request #106 from pingdynasty/feature/circularbuffer-updates
CircularBuffer updates
2 parents e9dc33d + 8a1e48e commit 267b96e

33 files changed

Lines changed: 874 additions & 321 deletions

HISTORY.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
v22.2
2+
-----
3+
* Added NoiseOscillator (whitenoise s+h)
4+
* Added PhaseShiftOscillator template
5+
* Added Biquad allpass filter configuration
6+
* Fixed Antialiased oscillators (Triangle, SquareWave, Ramp)
7+
* Added MorphingOscillator
8+
* AdjustableTapTempo interface changes
9+
* CircularBuffer improvements, added isFull()
10+
* Added InvertedRampOscillator
11+
* Added StateVariableFilter::setAllpass() and processLowMidHighBand()
112
* Improved realloc implementation
213
* Block based SineOscillator::generate() with FM
314
* Added test patches for automatic CI builds

LibSource/BiquadFilter.h

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ class FilterStage {
99
public:
1010
FloatArray coefficients;
1111
FloatArray state;
12-
static const float BESSEL_Q;
13-
static const float SALLEN_KEY_Q;
14-
static const float BUTTERWORTH_Q;
12+
static constexpr float BESSEL_Q = 0.57735026919f; // 1/sqrt(3)
13+
static constexpr float SALLEN_KEY_Q = 0.5f; // 1/2
14+
static constexpr float BUTTERWORTH_Q = 0.70710678118f; // 1/sqrt(2)
1515

1616
FilterStage(FloatArray co, FloatArray st) : coefficients(co), state(st){}
1717

@@ -27,6 +27,10 @@ class FilterStage {
2727
setBandPass(coefficients, fc*M_PI/sr, q);
2828
}
2929

30+
void setAllPass(float fc, float q, float sr){
31+
setAllPass(coefficients, fc*M_PI/sr, q);
32+
}
33+
3034
void setNotch(float fc, float q, float sr){
3135
setNotch(coefficients, fc*M_PI/sr, q);
3236
}
@@ -82,6 +86,16 @@ class FilterStage {
8286
coefficients[4] = - (1 - K / q + K * K) * norm;
8387
}
8488

89+
static void setAllPass(float* coefficients, float omega, float q){
90+
float K = tanf(omega);
91+
float norm = 1 / (1 + K / q + K * K);
92+
coefficients[0] = (1 - K / q + K * K) * norm;
93+
coefficients[1] = 2 * (K * K - 1) * norm;
94+
coefficients[2] = 1;
95+
coefficients[3] = coefficients[1];
96+
coefficients[4] = coefficients[0];
97+
}
98+
8599
static void setNotch(float* coefficients, float omega, float q){
86100
float K = tanf(omega);
87101
float norm = 1 / (1 + K / q + K * K);
@@ -310,6 +324,13 @@ class BiquadFilter : public SignalProcessor {
310324
}
311325
}
312326

327+
void processAllPass(FloatArray in, FloatArray fc, float q, FloatArray out){
328+
for(size_t i = 0; i < in.getSize(); i++){
329+
setAllPass(fc[i], q);
330+
out[i] = process(in[i]);
331+
}
332+
}
333+
313334
/* process a single sample and return the result */
314335
float process(float input){
315336
float output;
@@ -332,6 +353,11 @@ class BiquadFilter : public SignalProcessor {
332353
copyCoefficients();
333354
}
334355

356+
void setAllPass(float fc, float q){
357+
FilterStage::setAllPass(coefficients, fc*pioversr, q);
358+
copyCoefficients();
359+
}
360+
335361
void setNotch(float fc, float q){
336362
FilterStage::setNotch(coefficients, fc*pioversr, q);
337363
copyCoefficients();
@@ -463,8 +489,4 @@ class StereoBiquadFilter : public MultiBiquadFilter {
463489
}
464490
};
465491

466-
const float FilterStage::BESSEL_Q = 1/sqrtf(3); // 1/sqrt(3)
467-
const float FilterStage::SALLEN_KEY_Q = 0.5f; // 1/2
468-
const float FilterStage::BUTTERWORTH_Q = 1/sqrtf(2); // 1/sqrt(2)
469-
470492
#endif // __BiquadFilter_h__

LibSource/CircularBuffer.h

Lines changed: 67 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,96 +8,108 @@
88
#define FLOW_ASSERT(x, y)
99
#endif
1010

11-
template<typename T>
11+
template<typename DataType, typename IndexType = size_t>
1212
class CircularBuffer {
1313
protected:
14-
T* data;
15-
size_t size;
16-
size_t writepos = 0;
17-
size_t readpos = 0;
14+
DataType* data;
15+
IndexType size;
16+
IndexType writepos = 0;
17+
IndexType readpos = 0;
18+
bool empty = true;
1819
public:
1920
CircularBuffer(): data(NULL), size(0){}
20-
CircularBuffer(T* data, size_t size): data(data), size(size){}
21+
CircularBuffer(DataType* data, IndexType size): data(data), size(size){}
2122

22-
void setData(T* data, size_t len) {
23+
void setData(DataType* data, IndexType len) {
2324
this->data = data;
2425
size = len;
2526
}
2627

27-
size_t getSize() const {
28+
IndexType getSize() const {
2829
return size;
2930
}
3031

31-
T* getData() {
32+
DataType* getData() {
3233
return data;
3334
}
3435

3536
bool isEmpty() const {
36-
return writepos == readpos;
37+
return empty;
38+
}
39+
40+
bool isFull() const {
41+
return (writepos == readpos) && !empty;
3742
}
3843

39-
void write(T c){
44+
void write(DataType c){
45+
FLOW_ASSERT(getWriteCapacity() > 0, "overflow");
4046
data[writepos++] = c;
4147
if(writepos >= size)
4248
writepos = 0;
49+
empty = false;
4350
}
4451

45-
void write(T* source, size_t len){
52+
void write(DataType* source, IndexType len){
4653
FLOW_ASSERT(getWriteCapacity() >= len, "overflow");
47-
T* dest = getWriteHead();
48-
size_t rem = size-writepos;
49-
if(len > rem){
50-
memcpy(dest, source, rem*sizeof(T));
54+
DataType* dest = getWriteHead();
55+
IndexType rem = size-writepos;
56+
if(len >= rem){
57+
memcpy(dest, source, rem*sizeof(DataType));
5158
writepos = len-rem;
52-
memcpy(data, source+rem, writepos*sizeof(T));
59+
memcpy(data, source+rem, writepos*sizeof(DataType));
5360
}else{
54-
memcpy(dest, source, len*sizeof(T));
61+
memcpy(dest, source, len*sizeof(DataType));
5562
writepos += len;
5663
}
64+
empty = false;
5765
}
5866

59-
void writeAt(size_t index, T value){
67+
void writeAt(IndexType index, DataType value){
6068
data[index % size] = value;
6169
}
6270

63-
void overdub(T c){
71+
void overdub(DataType c){
6472
data[writepos++] += c;
6573
if(writepos >= size)
6674
writepos = 0;
75+
empty = false;
6776
}
6877

69-
void overdubAt(size_t index, T value){
78+
void overdubAt(IndexType index, DataType value){
7079
data[index % size] += value;
7180
}
7281

73-
T read(){
74-
T c = data[readpos++];
82+
DataType read(){
83+
FLOW_ASSERT(getReadCapacity() > 0, "underflow");
84+
DataType c = data[readpos++];
7585
if(readpos >= size)
7686
readpos = 0;
87+
empty = readpos == writepos;
7788
return c;
7889
}
7990

80-
void read(T* dst, size_t len){
91+
void read(DataType* dst, IndexType len){
8192
FLOW_ASSERT(getReadCapacity() >= len, "underflow");
82-
T* src = getReadHead();
83-
size_t rem = size-readpos;
93+
DataType* src = getReadHead();
94+
IndexType rem = size-readpos;
8495
if(len > rem){
85-
memcpy(dst, src, rem*sizeof(T));
96+
memcpy(dst, src, rem*sizeof(DataType));
8697
readpos = len-rem;
87-
memcpy(dst+rem, data, readpos*sizeof(T));
98+
memcpy(dst+rem, data, readpos*sizeof(DataType));
8899
}else{
89-
memcpy(dst, src, len*sizeof(T));
100+
memcpy(dst, src, len*sizeof(DataType));
90101
readpos += len;
91102
}
103+
empty = readpos == writepos;
92104
}
93105

94-
T readAt(size_t index){
106+
DataType readAt(IndexType index){
95107
return data[index % size];
96108
}
97109

98110
void skipUntilLast(char c){
99-
T* src = getReadHead();
100-
size_t rem = size-readpos;
111+
DataType* src = getReadHead();
112+
IndexType rem = size-readpos;
101113
for(int i=0; i<rem; ++i){
102114
if(src[i] != c){
103115
readpos += i;
@@ -111,40 +123,43 @@ class CircularBuffer {
111123
return;
112124
}
113125
}
126+
empty = readpos == writepos;
114127
}
115128

116-
size_t getWriteIndex(){
129+
IndexType getWriteIndex(){
117130
return writepos;
118131
}
119132

120-
void setWriteIndex(size_t pos){
133+
void setWriteIndex(IndexType pos){
121134
writepos = pos % size;
122135
}
123136

124-
T* getWriteHead(){
137+
DataType* getWriteHead(){
125138
return data+writepos;
126139
}
127140

128141
void moveWriteHead(int32_t samples){
129142
FLOW_ASSERT(getWriteCapacity() >= samples, "overflow");
130143
writepos = (writepos + samples) % size;
144+
empty = false;
131145
}
132146

133-
size_t getReadIndex(){
147+
IndexType getReadIndex(){
134148
return readpos;
135149
}
136150

137-
void setReadIndex(size_t pos){
151+
void setReadIndex(IndexType pos){
138152
readpos = pos % size;
139153
}
140154

141-
T* getReadHead(){
155+
DataType* getReadHead(){
142156
return data+readpos;
143157
}
144158

145159
void moveReadHead(int32_t samples){
146160
FLOW_ASSERT(getReadCapacity() < samples, "underflow");
147161
readpos = (readpos + samples) % size;
162+
empty = readpos == writepos;
148163
}
149164

150165
/**
@@ -157,61 +172,62 @@ class CircularBuffer {
157172
/**
158173
* Get the read index expressed as delay behind the write index.
159174
*/
160-
int getDelay(){
175+
IndexType getDelay() const {
161176
return (writepos-readpos+size) % size;
162177
}
163178

164179
/**
165180
* Write to buffer and read with a delay
166181
*/
167-
void delay(T* in, T* out, size_t len, int delay_samples){
182+
void delay(DataType* in, DataType* out, IndexType len, int delay_samples){
168183
setDelay(delay_samples); // set delay relative to where we start writing
169184
write(in, len);
170185
read(out, len);
171186
}
172187

173-
size_t getReadCapacity(){
174-
return (writepos + size - readpos) % size;
188+
IndexType getReadCapacity() const {
189+
return size - getWriteCapacity();
175190
}
176191

177-
size_t getWriteCapacity(){
178-
return size - getReadCapacity();
192+
IndexType getWriteCapacity() const {
193+
return size*empty + (readpos + size - writepos) % size;
179194
}
180195

181-
size_t getContiguousWriteCapacity(){
196+
IndexType getContiguousWriteCapacity() const {
182197
if(writepos < readpos)
183198
return readpos - writepos;
184199
else
185200
return size - writepos;
186201
}
187202

188-
size_t getContiguousReadCapacity(){
203+
IndexType getContiguousReadCapacity() const {
189204
if(writepos < readpos)
190205
return size - readpos;
191206
else
192207
return writepos - readpos;
193208
}
194209

195-
void setAll(const T value){
196-
for(size_t i=0; i<size; ++i)
210+
void setAll(const DataType value){
211+
for(IndexType i=0; i<size; ++i)
197212
data[i] = value;
198213
}
199214

200215
void reset(){
201216
readpos = writepos = 0;
217+
empty = true;
202218
}
203219

204220
void clear(){
205221
setAll(0);
206222
}
207223

208-
static CircularBuffer<T>* create(size_t len){
209-
CircularBuffer<T>* obj = new CircularBuffer<T>(new T[len], len);
224+
static CircularBuffer<DataType>* create(IndexType len){
225+
CircularBuffer<DataType>* obj = new CircularBuffer<DataType>(new DataType[len], len);
210226
obj->clear();
211227
return obj;
212228
}
213229

214-
static void destroy(CircularBuffer<T>* obj){
230+
static void destroy(CircularBuffer<DataType>* obj){
215231
delete[] obj->data;
216232
delete obj;
217233
}

LibSource/DelayProcessor.h

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ class FractionalDelayProcessor : public SignalProcessor {
6464
buffer.clear();
6565
}
6666
float process(float input){
67-
float sample = buffer.readAt(buffer.getWriteIndex() - delay);
67+
buffer.setDelay(delay);
6868
buffer.write(input);
69-
return sample;
69+
return buffer.read();
7070
}
7171
/**
7272
* Delay smoothly from the previous delay time to @param newDelay
@@ -129,7 +129,10 @@ class FastFractionalDelayProcessor : public SignalProcessor {
129129
};
130130

131131
/**
132-
* Delay line signal processor that crossfades to smooth changes in delay time.
132+
* Delay line signal processor that crossfades to ensure smooth changes in delay time.
133+
* Cross fade time in samples is equal to one block size.
134+
* Delay time should be updated at block rate, before calling the block-based process() method.
135+
* Sample based processing should not be used with this class.
133136
*/
134137
class CrossFadingDelayProcessor : public SignalProcessor {
135138
protected:
@@ -147,13 +150,6 @@ class CrossFadingDelayProcessor : public SignalProcessor {
147150
void clear(){
148151
ringbuffer->clear();
149152
}
150-
float process(float input){
151-
ringbuffer->write(input);
152-
float sample = ringbuffer->read();
153-
ringbuffer->setDelay(delay);
154-
sample += ringbuffer->read();
155-
return sample*0.5;
156-
}
157153
void process(FloatArray input, FloatArray output){
158154
ringbuffer->delay(input, output, input.getSize(), delay);
159155
}

0 commit comments

Comments
 (0)