Skip to content

Commit 85a82ac

Browse files
authored
Merge pull request #97 from pingdynasty/feature/audio-processors
Audio processors
2 parents 4dbae18 + 56a42db commit 85a82ac

63 files changed

Lines changed: 2081 additions & 923 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

GenSource/GenPatch.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,22 @@ class GenPatch : public Patch {
231231
for(int i=0; i<PatchMetadata::parameter_count; ++i){
232232
const PatchMetadata::Control& ctrl = PatchMetadata::parameters[i];
233233
PatchParameterId pid = (PatchParameterId)ctrl.id;
234-
registerParameter(pid, ctrl.name);
235234
if(ctrl.flags & CONTROL_OUTPUT){
236235
if(outputindex < nof_outs)
237236
outputs[outputindex] = OutputParameter(pid, ctrl.name, FloatArray(buffers[channels+outputindex], getBlockSize()));
238237
outputindex++;
239238
}
239+
size_t len = strlen(ctrl.name);
240+
if(ctrl.flags & CONTROL_OUTPUT && ctrl.name[len-1] != '>'){
241+
// add a > at end of name
242+
char name[len+2];
243+
strcpy(name, ctrl.name);
244+
name[len] = '>';
245+
name[len+1] = '\0';
246+
registerParameter(pid, name);
247+
}else{
248+
registerParameter(pid, ctrl.name);
249+
}
240250
}
241251
for(int i=0; i<PatchMetadata::button_count; ++i){
242252
const PatchMetadata::Control& ctrl = PatchMetadata::buttons[i];

GenSource/genlib_ops.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2222

2323
#include "genlib_common.h" // common to common code and any host code
2424
#include "genlib.h" // this file is different for different "hosts"
25+
#ifdef clamp
26+
#undef clamp
27+
#endif
2528

2629
//////////// genlib_ops.h ////////////
2730

HISTORY.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
1+
* Automatically add '>' to end of gen~ output parameters
2+
* Updated to use C++17
3+
* Use std::min/max/abs/clamp instead of macros for C++
4+
* Refactored AdsrEnvelope to Linear and Exponential versions
15
* Updated CMSIS libraries
26

37
v21.2
48
-----
59

10+
* Added FloatArray::softclip()
11+
* Added clamp(x, lo, hi) macro
12+
* Added TapTempo
13+
* Added DryWetProcessors
14+
* Added FeedbackProcessors
15+
* Added CrossFadingDelayProcessor
16+
* Added AudioBuffer::copyFrom(), copyTo(), multiply() and add()
617
* Added Sample oscillator
718
* Added Agnesi curve oscillator
819
* Added MPE processor

LibSource/AbstractSynth.h

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,20 @@ class AbstractSynth : public Synth, public MidiProcessor, public VelocityCurve {
1010
protected:
1111
uint8_t note = 60; // C4
1212
float pb = 0;
13-
float pb_range = 2;
13+
float pb_range = 2/8192.0f;
14+
float mod_range = 0.5/127.0f;
15+
float tuning = 440;
1416
public:
1517
virtual ~AbstractSynth(){}
18+
/**
19+
* Set frequency in Hertz for middle A (defaults to Stuttgart pitch, A440, 440 Hz)
20+
*/
21+
void setTuning(float value){
22+
tuning = value;
23+
}
24+
float getTuning(){
25+
return tuning;
26+
}
1627
/**
1728
* Set note in whole semitones
1829
*/
@@ -50,8 +61,14 @@ class AbstractSynth : public Synth, public MidiProcessor, public VelocityCurve {
5061
* Does not update the frequency; effective from next pitch bend change
5162
*/
5263
void setPitchBendRange(float range){
53-
this->pb_range = range;
54-
}
64+
this->pb_range = range/8192.0f;
65+
}
66+
/**
67+
* Set modulation depth range, from 0 to 1.0
68+
*/
69+
void setModulationDepthRange(float range){
70+
mod_range = range / 127.0f;
71+
}
5572
// MIDI handlers
5673
virtual void noteOn(MidiMessage msg) override {
5774
setNote(msg.getNote());
@@ -64,30 +81,34 @@ class AbstractSynth : public Synth, public MidiProcessor, public VelocityCurve {
6481
}
6582
virtual void controlChange(MidiMessage msg) override {
6683
if(msg.getControllerNumber() == MIDI_CC_MODULATION)
67-
setModulation(msg.getControllerValue()/127.0f);
84+
setModulation(msg.getControllerValue()/128.0f);
6885
else if(msg.getControllerNumber() == MIDI_ALL_NOTES_OFF)
6986
allNotesOff();
7087
}
7188
virtual void channelPressure(MidiMessage msg) override {
72-
setPressure(msg.getChannelPressure()/127.0f);
89+
setPressure(msg.getChannelPressure()/128.0f);
7390
}
7491
virtual void polyKeyPressure(MidiMessage msg) override {
75-
setPressure(msg.getPolyKeyPressure()/127.0f);
92+
setPressure(msg.getPolyKeyPressure()/128.0f);
93+
}
94+
virtual void modulate(MidiMessage msg) override {
95+
setModulation(mod_range * msg.getControllerValue());
7696
}
77-
virtual void setModulation(float modulation){} // default implementation does nothing
78-
virtual void setPressure(float pressure){}
7997
virtual void pitchbend(MidiMessage msg) override {
80-
setPitchBend(pb_range*msg.getPitchBend()/8192.0f);
98+
setPitchBend(pb_range * msg.getPitchBend());
8199
}
82100
virtual void allNotesOff(){
83101
gate(false);
84102
}
103+
// default implementations do nothing
104+
virtual void setModulation(float modulation){}
105+
virtual void setPressure(float pressure){}
85106
// static utility methods
86-
static inline float frequencyToNote(float freq){
87-
return 12 * log2f(freq / 440) + 69;
107+
inline float frequencyToNote(float freq){
108+
return 12 * log2f(freq / tuning) + 69;
88109
}
89-
static inline float noteToFrequency(float note){
90-
return 440 * exp2f((note - 69) / 12);
110+
inline float noteToFrequency(float note){
111+
return tuning * exp2f((note - 69) / 12);
91112
}
92113
};
93114

LibSource/AdsrEnvelope.h

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
#ifndef __ADSR_ENVELOPE_H
2+
#define __ADSR_ENVELOPE_H
3+
4+
#include "Envelope.h"
5+
6+
/**
7+
* ADSR Envelope, either linear or exponential
8+
*/
9+
template<bool linear>
10+
class AdsrEnvelope : public Envelope {
11+
protected:
12+
static const float MINLEVEL;
13+
enum EnvelopeStage { kAttack, kDecay, kSustain, kRelease, kIdle };
14+
enum EnvelopeTrigger { kGate, kTrigger };
15+
float calculateIncrement(float startValue, float endValue, float time);
16+
float increment(float level, float amount);
17+
float decrement(float level, float amount);
18+
public:
19+
AdsrEnvelope(float sampleRate) :
20+
sampleRate(sampleRate),
21+
stage(kIdle),
22+
trig(kGate),
23+
level(MINLEVEL),
24+
gateState(false),
25+
gateTime(-1) {
26+
setAttack(1/sampleRate);
27+
setDecay(1/sampleRate);
28+
setSustain(1.0);
29+
setRelease(1/sampleRate);
30+
setRetrigger(false);
31+
}
32+
33+
using Envelope::process;
34+
using Envelope::trigger;
35+
using SignalGenerator::generate;
36+
void setSampleRate(float value){
37+
sampleRate = value;
38+
}
39+
void setAttack(float value){
40+
attackIncrement = calculateIncrement(MINLEVEL, 1, value);
41+
}
42+
void setDecay(float value){
43+
decayIncrement = calculateIncrement(1, MINLEVEL, value);
44+
}
45+
void setRelease(float value){
46+
releaseIncrement = calculateIncrement(1, MINLEVEL, value);
47+
}
48+
void setSustain(float newSustain){
49+
sustain = newSustain;
50+
}
51+
void trigger(bool state, int delay){
52+
gate(state, delay);
53+
trig = kTrigger;
54+
}
55+
void setRetrigger(bool state){
56+
retrigger = state;
57+
}
58+
void gate(bool state){
59+
gate(state, 0);
60+
}
61+
void gate(bool state, int delay){
62+
if(gateState != state){
63+
gateTime = delay;
64+
gateState = state;
65+
}
66+
trig = kGate;
67+
}
68+
float getLevel(){
69+
return level;
70+
}
71+
void setLevel(float newLevel){
72+
level = newLevel;
73+
}
74+
/**
75+
* Produce the next envelope sample.
76+
*/
77+
float generate(){
78+
if(gateTime == 0){
79+
stage = kAttack;
80+
if(trig == kTrigger){
81+
gateState = false;
82+
}
83+
}
84+
if(gateTime >= 0){
85+
gateTime--; // this will stop at -1
86+
}
87+
switch (stage) {
88+
case kAttack:
89+
// attack ramp
90+
level = increment(level, attackIncrement);
91+
if(level >= 1.0){
92+
level = 1.0;
93+
stage = kDecay;
94+
}else if(gateState == false && trig == kGate){
95+
stage = kRelease;
96+
}
97+
break;
98+
case kDecay:
99+
// decay ramp
100+
level = decrement(level, decayIncrement);
101+
if(level <= sustain){
102+
level = sustain;
103+
if(trig == kGate){
104+
stage = kSustain;
105+
} else { // (trig == kTrigger)
106+
stage = kRelease;
107+
}
108+
} else if(gateState == false && trig == kGate){
109+
stage = kRelease;
110+
}
111+
break;
112+
case kSustain:
113+
level = sustain;
114+
if(gateState == false){
115+
stage = kRelease;
116+
}
117+
break;
118+
case kRelease:
119+
// release ramp
120+
level = decrement(level, releaseIncrement);
121+
if(level <= MINLEVEL){
122+
level = MINLEVEL;
123+
if (retrigger == true)
124+
trigger();
125+
else // (retrigger == false)
126+
stage = kIdle;
127+
}else if(gateState == true ){ // if during release the gate is on again, start over from the current level
128+
stage = kAttack;
129+
}
130+
break;
131+
case kIdle:
132+
level = MINLEVEL;
133+
if(gateState == true)
134+
stage = kAttack;
135+
break;
136+
}
137+
return level;
138+
}
139+
[[deprecated("use generate() instead.")]]
140+
float getNextSample(){
141+
return generate(); // increments envelope one step
142+
}
143+
[[deprecated("use generate() instead.")]]
144+
void getEnvelope(FloatArray output){
145+
generate(output); // increments envelope by output buffer length
146+
}
147+
[[deprecated("use process() instead.")]]
148+
void attenuate(FloatArray buf){
149+
process(buf, buf); // increments envelope by buffer length
150+
}
151+
static AdsrEnvelope<linear>* create(float sampleRate){
152+
return new AdsrEnvelope<linear>(sampleRate);
153+
}
154+
static void destroy(AdsrEnvelope<linear>* env){
155+
delete env;
156+
}
157+
protected:
158+
float sampleRate;
159+
EnvelopeStage stage;
160+
EnvelopeTrigger trig;
161+
bool retrigger;
162+
float level;
163+
float attackIncrement;
164+
float decayIncrement;
165+
float releaseIncrement;
166+
float sustain;
167+
bool gateState;
168+
int gateTime;
169+
};
170+
171+
template<>
172+
float AdsrEnvelope<true>::increment(float level, float amount){
173+
return level + amount;
174+
}
175+
176+
template<>
177+
float AdsrEnvelope<true>::decrement(float level, float amount){
178+
return level + amount;
179+
}
180+
181+
template<>
182+
float AdsrEnvelope<false>::increment(float level, float amount){
183+
return level + (1.01-level)*amount; // aim slightly higher to ensure we reach 1.0
184+
}
185+
186+
template<>
187+
float AdsrEnvelope<false>::decrement(float level, float amount){
188+
return level + level * amount;
189+
}
190+
191+
template<>
192+
float AdsrEnvelope<true>::calculateIncrement(float startValue, float endValue, float time){
193+
return (endValue-startValue)/(sampleRate*time+1);
194+
}
195+
196+
template<>
197+
float AdsrEnvelope<false>::calculateIncrement(float startValue, float endValue, float time) {
198+
// Ref: Christian Schoenebeck http://www.musicdsp.org/showone.php?id=189
199+
return (logf(endValue) - logf(startValue)) / (time*sampleRate+10);
200+
}
201+
202+
template<>
203+
const float AdsrEnvelope<true>::MINLEVEL = 0;
204+
205+
template<>
206+
const float AdsrEnvelope<false>::MINLEVEL = 0.00001; // -100dB
207+
208+
typedef AdsrEnvelope<true> LinearAdsrEnvelope;
209+
typedef AdsrEnvelope<false> ExponentialAdsrEnvelope;
210+
211+
#endif /* __ADSR_ENVELOPE_H */

LibSource/AgnesiOscillator.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,27 +35,31 @@ class AgnesiOscillator : public Oscillator {
3535
float getPhase(){
3636
return M_PI*x/N+M_PI;
3737
}
38+
void reset(){
39+
x = -N;
40+
}
3841
/**
39-
* Normalise offset and gain so that signal is between 0 and 1
42+
* Normalise offset and gain so that signal is between -1 and 1
4043
*/
4144
void normalise(){
4245
offset = agnesi(N, a);
43-
gain = 1/(agnesi(0, a) - offset);
46+
gain = 2/(agnesi(0, a) - offset);
47+
offset += 0.5;
4448
}
4549
float generate(){
4650
float y = agnesi(x, a);
4751
x += incr;
4852
if(x > N)
4953
x -= 2*N;
50-
return gain*(y-offset);
54+
return clamp(gain*(y-offset), -1.0f, 1.0f);
5155
}
5256
float generate(float fm){
5357
// modulate coefficient 'a' instead of rate
54-
float y = agnesi(x, a+fm);
58+
float y = agnesi(x, a*(1+fm));
5559
x += incr;
5660
if(x > N)
5761
x -= 2*N;
58-
return gain*(y-offset);
62+
return clamp(gain*(y-offset), -1.0f, 1.0f);
5963
}
6064
using SignalGenerator::generate;
6165
static AgnesiOscillator* create(float sr, float a=0.5, float N=5){

0 commit comments

Comments
 (0)