arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
AudioEffect.h
1#pragma once
2#include "AudioParameters.h"
3#include "PitchShift.h"
4#include "AudioLogger.h"
5#include "AudioTools/CoreAudio/AudioTypes.h"
6#include "AudioTools/CoreAudio/AudioOutput.h"
7#include <stdint.h>
8
9namespace audio_tools {
10
11// we use int16_t for our effects
12typedef int16_t effect_t;
13
22public:
23 AudioEffect() = default;
24 virtual ~AudioEffect() = default;
25
27 virtual effect_t process(effect_t in) = 0;
28
30 virtual void setActive(bool value) { active_flag = value; }
31
33 virtual bool active() { return active_flag; }
34
35 virtual AudioEffect *clone() = 0;
36
38 int id() { return id_value; }
39
41 void setId(int id) { this->id_value = id; }
42
43protected:
44 bool active_flag = true;
45 int id_value = -1;
46
47 void copyParent(AudioEffect *copy) {
48 id_value = copy->id_value;
49 active_flag = copy->active_flag;
50 }
51
53 int16_t clip(int32_t in, int16_t clipLimit = 32767,
54 int16_t resultLimit = 32767) {
55 int32_t result = in;
56 if (result > clipLimit) {
57 result = resultLimit;
58 }
59 if (result < -clipLimit) {
60 result = -resultLimit;
61 }
62 return result;
63 }
64};
65
74class Boost : public AudioEffect, public VolumeSupport {
75public:
78 Boost(float volume = 1.0) { setVolume(volume); }
79
80 Boost(const Boost &copy) = default;
81
82 effect_t process(effect_t input) {
83 if (!active())
84 return input;
85 int32_t result = volume() * input;
86 // clip to int16_t
87 return clip(result);
88 }
89
90 Boost *clone() { return new Boost(*this); }
91
92};
93
101class Distortion : public AudioEffect {
102public:
104 Distortion(int16_t clipThreashold = 4990, int16_t maxInput = 6500) {
105 p_clip_threashold = clipThreashold;
106 max_input = maxInput;
107 }
108
109 Distortion(const Distortion &copy) = default;
110
111 void setClipThreashold(int16_t th) { p_clip_threashold = th; }
112
113 int16_t clipThreashold() { return p_clip_threashold; }
114
115 void setMaxInput(int16_t maxInput) { max_input = maxInput; }
116
117 int16_t maxInput() { return max_input; }
118
119 effect_t process(effect_t input) {
120 if (!active())
121 return input;
122 // the input signal is 16bits (values from -32768 to +32768
123 // the value of input is clipped to the distortion_threshold value
124 return clip(input, p_clip_threashold, max_input);
125 }
126
127 Distortion *clone() { return new Distortion(*this); }
128
129protected:
130 int16_t p_clip_threashold;
131 int16_t max_input;
132};
133
141class Fuzz : public AudioEffect {
142public:
144 Fuzz(float fuzzEffectValue = 6.5, uint16_t maxOut = 300) {
145 p_effect_value = fuzzEffectValue;
146 max_out = maxOut;
147 }
148
149 Fuzz(const Fuzz &copy) = default;
150
151 void setFuzzEffectValue(float v) { p_effect_value = v; }
152
153 float fuzzEffectValue() { return p_effect_value; }
154
155 void setMaxOut(uint16_t v) { max_out = v; }
156
157 uint16_t maxOut() { return max_out; }
158
159 effect_t process(effect_t input) {
160 if (!active())
161 return input;
162 float v = p_effect_value;
163 int32_t result = clip(v * input);
164 return map(result * v, -32768, +32767, -max_out, max_out);
165 }
166
167 Fuzz *clone() { return new Fuzz(*this); }
168
169protected:
170 float p_effect_value;
171 uint16_t max_out;
172};
173
181class Tremolo : public AudioEffect {
182public:
185 Tremolo(int16_t duration_ms = 2000, uint8_t depthPercent = 50,
186 uint32_t sampleRate = 44100) {
187 this->duration_ms = duration_ms;
188 this->sampleRate = sampleRate;
189 this->p_percent = depthPercent;
190 int32_t rate_count = sampleRate * duration_ms / 1000;
191 rate_count_half = rate_count / 2;
192 }
193
194 Tremolo(const Tremolo &copy) = default;
195
196 void setDuration(int16_t ms) {
197 this->duration_ms = ms;
198 int32_t rate_count = sampleRate * ms / 1000;
199 rate_count_half = rate_count / 2;
200 }
201
202 int16_t duration() { return duration_ms; }
203
204 void setDepth(uint8_t percent) { p_percent = percent; }
205
206 uint8_t depth() { return p_percent; }
207
208 effect_t process(effect_t input) {
209 if (!active())
210 return input;
211
212 // limit value to max 100% and calculate factors
213 float tremolo_depth = p_percent > 100 ? 1.0 : 0.01 * p_percent;
214 float signal_depth = (100.0 - p_percent) / 100.0;
215
216 float tremolo_factor = tremolo_depth / rate_count_half;
217 int32_t out = (signal_depth * input) + (tremolo_factor * count * input);
218
219 // saw tooth shaped counter
220 count += inc;
221 if (count >= rate_count_half) {
222 inc = -1;
223 } else if (count <= 0) {
224 inc = +1;
225 }
226
227 return clip(out);
228 }
229
230 Tremolo *clone() { return new Tremolo(*this); }
231
232protected:
233 int16_t duration_ms;
234 uint32_t sampleRate;
235 int32_t count = 0;
236 int16_t inc = 1;
237 int32_t rate_count_half; // number of samples for on raise and fall
238 uint8_t p_percent;
239};
240
249class Delay : public AudioEffect {
250public:
252 Delay(uint16_t duration_ms = 1000, float depth = 0.5,
253 float feedbackAmount = 1.0, uint32_t sampleRate = 44100) {
254 setSampleRate(sampleRate);
255 setFeedback(feedbackAmount);
256 setDepth(depth);
257 setDuration(duration_ms);
258 }
259
260 Delay(const Delay &copy) {
261 setSampleRate(copy.sampleRate);
262 setFeedback(copy.feedback);
263 setDepth(copy.depth);
264 setDuration(copy.duration);
265 };
266
267 void setDuration(int16_t dur) {
268 duration = dur;
269 updateBufferSize();
270 }
271
272 int16_t getDuration() { return duration; }
273
274 void setDepth(float value) {
275 depth = value;
276 if (depth > 1.0f)
277 depth = 1.0f;
278 if (depth < 0.0f)
279 depth = 0.0f;
280 }
281
282 float getDepth() { return depth; }
283
284 void setFeedback(float feed) {
285 feedback = feed;
286 if (feedback > 1.0f)
287 feedback = 1.0f;
288 if (feedback < 0.0f)
289 feedback = 0.0f;
290 }
291
292 float getFeedback() { return feedback; }
293
294 void setSampleRate(int32_t sample) {
295 sampleRate = sample;
296 updateBufferSize();
297 }
298
299 float getSampleRate() { return sampleRate; }
300
301 effect_t process(effect_t input) {
302 if (!active())
303 return input;
304
305 // Read last audio sample in each delay line
306 int32_t delayed_value = buffer[delay_line_index];
307
308 // Mix the above with current audio and write the results back to output
309 int32_t out = ((1.0f - depth) * input) + (depth * delayed_value);
310
311 // Update each delay line
312 buffer[delay_line_index] = clip(feedback * (delayed_value + input));
313
314 // Finally, update the delay line index
315 if (delay_line_index++ >= delay_len_samples) {
316 delay_line_index = 0;
317 }
318 return clip(out);
319 }
320
321 Delay *clone() { return new Delay(*this); }
322
323protected:
324 Vector<effect_t> buffer{0};
325 float feedback = 0.0f, duration = 0.0f, sampleRate = 0.0f, depth = 0.0f;
326 size_t delay_len_samples = 0;
327 size_t delay_line_index = 0;
328
329 void updateBufferSize() {
330 if (sampleRate > 0 && duration > 0) {
331 size_t newSampleCount = sampleRate * duration / 1000;
332 if (newSampleCount != delay_len_samples) {
333 delay_len_samples = newSampleCount;
334 buffer.resize(delay_len_samples);
335 memset(buffer.data(),0,delay_len_samples*sizeof(effect_t));
336 LOGD("sample_count: %u", (unsigned)delay_len_samples);
337 }
338 }
339 }
340};
341
357class ADSRGain : public AudioEffect {
358public:
359 ADSRGain(float attack = 0.001, float decay = 0.001, float sustainLevel = 0.5,
360 float release = 0.005, float boostFactor = 1.0) {
361 this->factor = boostFactor;
362 adsr = new ADSR(attack, decay, sustainLevel, release);
363 }
364
365 ADSRGain(const ADSRGain &ref) {
366 adsr = new ADSR(*(ref.adsr));
367 factor = ref.factor;
368 copyParent((AudioEffect *)&ref);
369 };
370
371 virtual ~ADSRGain() { delete adsr; }
372
373 void setAttackRate(float a) { adsr->setAttackRate(a); }
374
375 float attackRate() { return adsr->attackRate(); }
376
377 void setDecayRate(float d) { adsr->setDecayRate(d); }
378
379 float decayRate() { return adsr->decayRate(); }
380
381 void setSustainLevel(float s) { adsr->setSustainLevel(s); }
382
383 float sustainLevel() { return adsr->sustainLevel(); }
384
385 void setReleaseRate(float r) { adsr->setReleaseRate(r); }
386
387 float releaseRate() { return adsr->releaseRate(); }
388
389 void keyOn(float tgt = 0) { adsr->keyOn(tgt); }
390
391 void keyOff() { adsr->keyOff(); }
392
393 effect_t process(effect_t input) {
394 if (!active())
395 return input;
396 effect_t result = factor * adsr->tick() * input;
397 return result;
398 }
399
400 bool isActive() { return adsr->isActive(); }
401
402 ADSRGain *clone() { return new ADSRGain(*this); }
403
404protected:
405 ADSR *adsr;
406 float factor;
407};
408
415class PitchShift : public AudioEffect {
416public:
419 PitchShift(float shift_value = 1.0, int buffer_size = 1000) {
420 effect_value = shift_value;
421 size = buffer_size;
422 buffer.resize(buffer_size);
423 buffer.setIncrement(shift_value);
424 }
425
426 PitchShift(const PitchShift &ref) {
427 size = ref.size;
428 effect_value = ref.effect_value;
429 buffer.resize(size);
430 buffer.setIncrement(effect_value);
431 };
432
433 float value() { return effect_value; }
434
435 void setValue(float value) {
436 effect_value = value;
437 buffer.setIncrement(value);
438 }
439
440 effect_t process(effect_t input) {
441 if (!active())
442 return input;
443 buffer.write(input);
444 effect_t result;
445 buffer.read(result);
446 return result;
447 }
448
449 PitchShift *clone() { return new PitchShift(*this); }
450
451protected:
452 VariableSpeedRingBuffer<int16_t> buffer;
453 float effect_value;
454 int size;
455};
456
457
465class Compressor : public AudioEffect {
466public:
468 Compressor(const Compressor &copy) = default;
469
471 Compressor(uint32_t sampleRate = 44100, uint16_t attackMs=30, uint16_t releaseMs=20, uint16_t holdMs=10, uint8_t thresholdPercent=10, float compressionRatio=0.5){
472 //assuming 1 sample = 1/96kHz = ~10us
473 //Attack -> 30 ms -> 3000
474 //Release -> 20 ms -> 2000
475 //Hold -> 10ms -> 1000
476 sample_rate = sample_rate * attackMs / 1000;
477 attack_count = sample_rate * attackMs / 1000;
478 release_count = sample_rate * releaseMs / 1000;
479 hold_count = sample_rate * holdMs / 1000;
480
481 //threshold -20dB below limit -> 0.1 * 2^31
482 threshold = 0.01f * thresholdPercent * NumberConverter::maxValueT<effect_t>();
483 //compression ratio: 6:1 -> -6dB = 0.5
484 gainreduce = compressionRatio;
485 //initial gain = 1.0 -> no compression
486 gain = 1.0f;
487 recalculate();
488 }
489
491 void setAttack(uint16_t attackMs){
492 attack_count = sample_rate * attackMs / 1000;
493 recalculate();
494 }
495
497 void setRelease(uint16_t releaseMs){
498 release_count = sample_rate * releaseMs / 1000;
499 recalculate();
500 }
501
503 void setHold(uint16_t holdMs){
504 hold_count = sample_rate * holdMs / 1000;
505 recalculate();
506 }
507
509 void setThresholdPercent(uint8_t thresholdPercent){
510 threshold = 0.01f * thresholdPercent * NumberConverter::maxValueT<effect_t>();
511 }
512
514 void setCompressionRatio(float compressionRatio){
515 if (compressionRatio < 1.0f){
516 gainreduce = compressionRatio;
517 }
518 recalculate();
519 }
520
522 effect_t process(effect_t input) {
523 if (!active())
524 return input;
525 return compress(input);
526 }
527
528 Compressor *clone() { return new Compressor(*this); }
529
530protected:
531 enum CompStates {S_NoOperation, S_Attack, S_GainReduction, S_Release };
532 enum CompStates state = S_NoOperation;
533
534 int32_t attack_count, release_count, hold_count, timeout;
535 float gainreduce, gain_step_attack, gain_step_release, gain, threshold;
536 uint32_t sample_rate;
537
538 void recalculate() {
539 gain_step_attack = (1.0f - gainreduce) / attack_count;
540 gain_step_release = (1.0f - gainreduce) / release_count;
541 }
542
543 float compress(float inSampleF){
544 if (fabs(inSampleF) > threshold) {
545 if (gain >= gainreduce) {
546 if (state==S_NoOperation) {
547 state=S_Attack;
548 timeout = attack_count;
549 }
550 else if (state==S_Release) {
551 state=S_Attack;
552 timeout = attack_count;
553 }
554 }
555 if (state==S_GainReduction) timeout = hold_count;
556
557 }
558
559 if (fabs(inSampleF) < threshold && gain <= 1.0f) {
560 if ( timeout==0 && state==S_GainReduction) {
561 state=S_Release;
562 timeout = release_count;
563 }
564 }
565
566 switch (state) {
567 case S_Attack:
568 if ( timeout>0 && gain > gainreduce) {
569 gain -= gain_step_attack;
570 timeout--;
571 }
572 else {
573 state=S_GainReduction;
574 timeout = hold_count;
575 }
576 break;
577
578
579 case S_GainReduction:
580 if ( timeout>0) timeout--;
581 else {
582 state=S_Release;
583 timeout = release_count;
584 }
585 break;
586
587
588 case S_Release:
589 if ( timeout>0 && gain<1.0f) {
590 timeout--;
591 gain += gain_step_release;
592 }
593 else {
594 state=S_NoOperation;
595 }
596 break;
597
598 case S_NoOperation:
599 if (gain < 1.0f) gain = 1.0f;
600 break;
601
602 default:
603 break;
604
605 }
606
607 float outSampleF = gain * inSampleF;
608 return outSampleF;
609 }
610
611};
612
613
614} // namespace audio_tools
ADSR Envelope: Attack, Decay, Sustain and Release. Attack is the time taken for initial run-up oeffec...
Definition AudioEffect.h:357
effect_t process(effect_t input)
calculates the effect output from the input
Definition AudioEffect.h:393
Generates ADSR values between 0.0 and 1.0.
Definition AudioParameters.h:51
Abstract Base class for Sound Effects.
Definition AudioEffect.h:21
virtual bool active()
determines if the effect is active
Definition AudioEffect.h:33
virtual void setActive(bool value)
sets the effect active/inactive
Definition AudioEffect.h:30
void setId(int id)
Allows to identify an effect.
Definition AudioEffect.h:41
int id()
Allows to identify an effect.
Definition AudioEffect.h:38
virtual effect_t process(effect_t in)=0
calculates the effect output from the input
int16_t clip(int32_t in, int16_t clipLimit=32767, int16_t resultLimit=32767)
generic clipping method
Definition AudioEffect.h:53
Boost AudioEffect.
Definition AudioEffect.h:74
effect_t process(effect_t input)
calculates the effect output from the input
Definition AudioEffect.h:82
Boost(float volume=1.0)
Definition AudioEffect.h:78
Compressor inspired by https://github.com/YetAnotherElectronicsChannel/STM32_DSP_COMPRESSOR/blob/mast...
Definition AudioEffect.h:465
void setAttack(uint16_t attackMs)
Defines the attack duration in ms.
Definition AudioEffect.h:491
effect_t process(effect_t input)
Processes the sample.
Definition AudioEffect.h:522
void setRelease(uint16_t releaseMs)
Defines the release duration in ms.
Definition AudioEffect.h:497
Compressor(uint32_t sampleRate=44100, uint16_t attackMs=30, uint16_t releaseMs=20, uint16_t holdMs=10, uint8_t thresholdPercent=10, float compressionRatio=0.5)
Default Constructor.
Definition AudioEffect.h:471
void setHold(uint16_t holdMs)
Defines the hold duration in ms.
Definition AudioEffect.h:503
Compressor(const Compressor &copy)=default
Copy Constructor.
void setCompressionRatio(float compressionRatio)
Defines the compression ratio from 0 to 1.
Definition AudioEffect.h:514
void setThresholdPercent(uint8_t thresholdPercent)
Defines the threshod in %.
Definition AudioEffect.h:509
Delay/Echo AudioEffect. See https://wiki.analog.com/resources/tools-software/sharc-audio-module/barem...
Definition AudioEffect.h:249
effect_t process(effect_t input)
calculates the effect output from the input
Definition AudioEffect.h:301
Delay(uint16_t duration_ms=1000, float depth=0.5, float feedbackAmount=1.0, uint32_t sampleRate=44100)
e.g. depth=0.5, ms=1000, sampleRate=44100
Definition AudioEffect.h:252
Distortion AudioEffect.
Definition AudioEffect.h:101
Distortion(int16_t clipThreashold=4990, int16_t maxInput=6500)
Distortion Constructor: e.g. use clipThreashold 4990 and maxInput=6500.
Definition AudioEffect.h:104
effect_t process(effect_t input)
calculates the effect output from the input
Definition AudioEffect.h:119
Fuzz AudioEffect.
Definition AudioEffect.h:141
effect_t process(effect_t input)
calculates the effect output from the input
Definition AudioEffect.h:159
Fuzz(float fuzzEffectValue=6.5, uint16_t maxOut=300)
Fuzz Constructor: use e.g. effectValue=6.5; maxOut = 300.
Definition AudioEffect.h:144
Shifts the pitch by the indicated step size: e.g. 2 doubles the pitch.
Definition AudioEffect.h:415
effect_t process(effect_t input)
calculates the effect output from the input
Definition AudioEffect.h:440
PitchShift(float shift_value=1.0, int buffer_size=1000)
Definition AudioEffect.h:419
Tremolo AudioEffect.
Definition AudioEffect.h:181
effect_t process(effect_t input)
calculates the effect output from the input
Definition AudioEffect.h:208
Tremolo(int16_t duration_ms=2000, uint8_t depthPercent=50, uint32_t sampleRate=44100)
Definition AudioEffect.h:185
bool write(T sample)
write add an entry to the buffer
Definition PitchShift.h:265
bool read(T &result)
reads a single value
Definition PitchShift.h:245
Supports the setting and getting of the volume.
Definition AudioTypes.h:189
virtual float volume()
provides the actual volume in the range of 0.0f to 1.0f
Definition AudioTypes.h:192
virtual bool setVolume(float volume)
define the actual volume in the range of 0.0f to 1.0f
Definition AudioTypes.h:194
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
long map(long x, long in_min, long in_max, long out_min, long out_max)
Maps input to output values.
Definition NoArduino.h:188