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