Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
900303a
added AudioBuffer::copyFrom(), copyTo() ::multiply()
May 13, 2021
ae3fc2f
initialise member fields
May 13, 2021
690a14c
added DryWetProcessor, FeedbackProcessor, TapTempo
May 13, 2021
89bc953
added fixed interpolated delay method
May 13, 2021
ad74309
updated FractionalDelay and added CrossFadingDelay
May 13, 2021
30bf6ca
added AudioBuffer::copyFrom(), copyTo() ::multiply()
May 13, 2021
5883265
writing history
May 13, 2021
738147a
added sustain
May 14, 2021
7ffcfbd
added sustain
May 14, 2021
eba3aec
fix typos
May 14, 2021
4f5450c
fix typos
May 14, 2021
566106c
added softclip method
May 18, 2021
a37f6b1
added 'clamp()
May 18, 2021
707aedc
updated history
May 18, 2021
35c5bbb
prevent clashing definition of 'clamp'
May 24, 2021
110c4fd
prevent clashing definition of 'clamp'
May 24, 2021
ba96a92
prevent clashing definition of 'clamp'
May 24, 2021
59f577f
added scalar FloatBuffer::add()
May 25, 2021
ac231d2
interpolated block read methods
May 25, 2021
f9bc21a
added sustain and SignalProcessor subclasses
May 25, 2021
b4b6efe
added SignalProcessor subclasses
May 25, 2021
f574177
random phase in GaussianNoiseGennerator
May 25, 2021
75d8d6d
refactored oscillator fm
May 25, 2021
51ebf9b
changed divs to muls
May 25, 2021
82ee285
add trailing > to metadata output parameter names
May 26, 2021
2352ccb
refactored voice allocators/processors
May 26, 2021
63a2372
fixed typos
May 27, 2021
da5acaf
added dynamic voice allocation for master channel
May 28, 2021
943a5dc
prevent clobbering fm array
May 28, 2021
5b9795c
added create() methods, fixed destroy()
May 28, 2021
0166291
added create() methods, fixed destroy()
May 28, 2021
9aab00c
Added interpolation templates. Refactored CircularBuffer into Interpo…
May 28, 2021
de3131e
added InterpolatingCircularFloatBuffer::create/destroy methods
Jun 2, 2021
2d1ffe1
changed CrossFadingDelayProcessor to use single circular buffer with …
Jun 7, 2021
df1360c
added CrossFadingCircularBuffer
Jun 7, 2021
db3b134
signedness fixes
Jun 9, 2021
a404e94
fixed create/destroy methods
Jun 9, 2021
816e253
TapTempo::get/setSamples() changed to get/setPeriodInSamples()
Jun 9, 2021
e195865
refactored circular buffers and delay lines
Jun 9, 2021
b3ba57b
fix signedness warning
Jun 9, 2021
a7ea6a9
fix signedness warnings and use destroy instead of delete
Jun 9, 2021
6edf705
check all leaks with valgrind and track origins
Jun 9, 2021
48777f2
set -O3 optimisation
Jun 9, 2021
1a4f70a
added clear() methods
Jun 18, 2021
ea8380b
bug fixes
Jun 18, 2021
bbee801
updated to produce output range [-1, 1]
Jun 25, 2021
d5b49bb
size doesn't have to be const
Jun 25, 2021
b6bfef5
refactored oscillators
Jun 25, 2021
e0d98fb
changed min/max/clamp macros to use std::min/max/clamp from algorithm
Jun 25, 2021
eee41cc
revert to macros for c only
Jun 25, 2021
04b1bef
fix some tests
Jun 25, 2021
8832738
changed min/max/clamp macros to use std::min/max/clamp from algorithm
Jun 25, 2021
827bd7e
added required reset()
Jun 25, 2021
1d77f71
fix includes
Jun 25, 2021
aaf22f0
use gnu++17 to support std::clamp (and strnlen)
Jun 25, 2021
654ae68
divide by 128.0 for 7-bit range [0,1)
Jun 25, 2021
1d04971
prevent std::max warnings
Jul 1, 2021
2995459
normalise phase increment in polyblep method
Jul 1, 2021
561cd93
fixed TapTempo::setFrequency() (was setting period instead of frequency)
Jul 1, 2021
74e7875
updated with latest definitions
Jul 1, 2021
7ab6f8e
AdsrEnvelope renamed LinearAdsrEnvelope
Jul 2, 2021
eb0708b
updated doc strings
Jul 2, 2021
c95040a
fix adsr test
Jul 2, 2021
da8e25d
clamp outputs to [1,1] range
Jul 2, 2021
ca25498
added WavFile::getAudioFormat()
Jul 6, 2021
4ee05ae
envelope changes
Jul 7, 2021
a7e66ff
fix envelope test include
Jul 7, 2021
51cf822
changed time basis calculation
Jul 7, 2021
d96ceb9
changed to decimal CC assignments, added RPN message types
Jul 12, 2021
da79100
changed to use oscillator template
Jul 12, 2021
a749a99
added RPN modulation depth, pitch bend range and tuning
Jul 12, 2021
1580d2b
added MidiProcessor::sustain() and modulate() callbacks
Jul 12, 2021
569f5d6
added/refactored Triangle/Ramp/SquareWave/VariableShape oscillators
Jul 12, 2021
d2f0aef
Merge branch 'develop' into feature/audio-processors
Jul 12, 2021
b38c45b
remove debug message
Jul 12, 2021
9ddd11e
added block based processing
Jul 12, 2021
44aaa9d
only set parameter value if value is initialised
Jul 13, 2021
b3b711a
check if resource exists
Jul 13, 2021
7713555
doc comments
Jul 14, 2021
fa8a5bc
adjusted adjustable waveshape
Jul 14, 2021
5efa373
include PATCHNAME in compilation and in metadata
Jul 14, 2021
a59411f
fix default parameter / button count
Jul 14, 2021
0f579bb
updated history
Aug 26, 2021
bde8791
Merge branch 'develop' into feature/audio-processors
Sep 22, 2021
57accd2
fix merge mistakes
Sep 22, 2021
1d595c7
updated CircularBuffer from OpenWare project
Oct 8, 2021
56a42db
updated links
Oct 8, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion GenSource/GenPatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,22 @@ class GenPatch : public Patch {
for(int i=0; i<PatchMetadata::parameter_count; ++i){
const PatchMetadata::Control& ctrl = PatchMetadata::parameters[i];
PatchParameterId pid = (PatchParameterId)ctrl.id;
registerParameter(pid, ctrl.name);
if(ctrl.flags & CONTROL_OUTPUT){
if(outputindex < nof_outs)
outputs[outputindex] = OutputParameter(pid, ctrl.name, FloatArray(buffers[channels+outputindex], getBlockSize()));
outputindex++;
}
size_t len = strlen(ctrl.name);
if(ctrl.flags & CONTROL_OUTPUT && ctrl.name[len-1] != '>'){
// add a > at end of name
char name[len+2];
strcpy(name, ctrl.name);
name[len] = '>';
name[len+1] = '\0';
registerParameter(pid, name);
}else{
registerParameter(pid, ctrl.name);
}
}
for(int i=0; i<PatchMetadata::button_count; ++i){
const PatchMetadata::Control& ctrl = PatchMetadata::buttons[i];
Expand Down
3 changes: 3 additions & 0 deletions GenSource/genlib_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "genlib_common.h" // common to common code and any host code
#include "genlib.h" // this file is different for different "hosts"
#ifdef clamp
#undef clamp
#endif

//////////// genlib_ops.h ////////////

Expand Down
11 changes: 11 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
* Automatically add '>' to end of gen~ output parameters
* Updated to use C++17
* Use std::min/max/abs/clamp instead of macros for C++
* Refactored AdsrEnvelope to Linear and Exponential versions
* Updated CMSIS libraries

v21.2
-----

* Added FloatArray::softclip()
* Added clamp(x, lo, hi) macro
* Added TapTempo
* Added DryWetProcessors
* Added FeedbackProcessors
* Added CrossFadingDelayProcessor
* Added AudioBuffer::copyFrom(), copyTo(), multiply() and add()
* Added Sample oscillator
* Added Agnesi curve oscillator
* Added MPE processor
Expand Down
47 changes: 34 additions & 13 deletions LibSource/AbstractSynth.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,20 @@ class AbstractSynth : public Synth, public MidiProcessor, public VelocityCurve {
protected:
uint8_t note = 60; // C4
float pb = 0;
float pb_range = 2;
float pb_range = 2/8192.0f;
float mod_range = 0.5/127.0f;
float tuning = 440;
public:
virtual ~AbstractSynth(){}
/**
* Set frequency in Hertz for middle A (defaults to Stuttgart pitch, A440, 440 Hz)
*/
void setTuning(float value){
tuning = value;
}
float getTuning(){
return tuning;
}
/**
* Set note in whole semitones
*/
Expand Down Expand Up @@ -50,8 +61,14 @@ class AbstractSynth : public Synth, public MidiProcessor, public VelocityCurve {
* Does not update the frequency; effective from next pitch bend change
*/
void setPitchBendRange(float range){
this->pb_range = range;
}
this->pb_range = range/8192.0f;
}
/**
* Set modulation depth range, from 0 to 1.0
*/
void setModulationDepthRange(float range){
mod_range = range / 127.0f;
}
// MIDI handlers
virtual void noteOn(MidiMessage msg) override {
setNote(msg.getNote());
Expand All @@ -64,30 +81,34 @@ class AbstractSynth : public Synth, public MidiProcessor, public VelocityCurve {
}
virtual void controlChange(MidiMessage msg) override {
if(msg.getControllerNumber() == MIDI_CC_MODULATION)
setModulation(msg.getControllerValue()/127.0f);
setModulation(msg.getControllerValue()/128.0f);
else if(msg.getControllerNumber() == MIDI_ALL_NOTES_OFF)
allNotesOff();
}
virtual void channelPressure(MidiMessage msg) override {
setPressure(msg.getChannelPressure()/127.0f);
setPressure(msg.getChannelPressure()/128.0f);
}
virtual void polyKeyPressure(MidiMessage msg) override {
setPressure(msg.getPolyKeyPressure()/127.0f);
setPressure(msg.getPolyKeyPressure()/128.0f);
}
virtual void modulate(MidiMessage msg) override {
setModulation(mod_range * msg.getControllerValue());
}
virtual void setModulation(float modulation){} // default implementation does nothing
virtual void setPressure(float pressure){}
virtual void pitchbend(MidiMessage msg) override {
setPitchBend(pb_range*msg.getPitchBend()/8192.0f);
setPitchBend(pb_range * msg.getPitchBend());
}
virtual void allNotesOff(){
gate(false);
}
// default implementations do nothing
virtual void setModulation(float modulation){}
virtual void setPressure(float pressure){}
// static utility methods
static inline float frequencyToNote(float freq){
return 12 * log2f(freq / 440) + 69;
inline float frequencyToNote(float freq){
return 12 * log2f(freq / tuning) + 69;
}
static inline float noteToFrequency(float note){
return 440 * exp2f((note - 69) / 12);
inline float noteToFrequency(float note){
return tuning * exp2f((note - 69) / 12);
}
};

Expand Down
211 changes: 211 additions & 0 deletions LibSource/AdsrEnvelope.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#ifndef __ADSR_ENVELOPE_H
#define __ADSR_ENVELOPE_H

#include "Envelope.h"

/**
* ADSR Envelope, either linear or exponential
*/
template<bool linear>
class AdsrEnvelope : public Envelope {
protected:
static const float MINLEVEL;
enum EnvelopeStage { kAttack, kDecay, kSustain, kRelease, kIdle };
enum EnvelopeTrigger { kGate, kTrigger };
float calculateIncrement(float startValue, float endValue, float time);
float increment(float level, float amount);
float decrement(float level, float amount);
public:
AdsrEnvelope(float sampleRate) :
sampleRate(sampleRate),
stage(kIdle),
trig(kGate),
level(MINLEVEL),
gateState(false),
gateTime(-1) {
setAttack(1/sampleRate);
setDecay(1/sampleRate);
setSustain(1.0);
setRelease(1/sampleRate);
setRetrigger(false);
}

using Envelope::process;
using Envelope::trigger;
using SignalGenerator::generate;
void setSampleRate(float value){
sampleRate = value;
}
void setAttack(float value){
attackIncrement = calculateIncrement(MINLEVEL, 1, value);
}
void setDecay(float value){
decayIncrement = calculateIncrement(1, MINLEVEL, value);
}
void setRelease(float value){
releaseIncrement = calculateIncrement(1, MINLEVEL, value);
}
void setSustain(float newSustain){
sustain = newSustain;
}
void trigger(bool state, int delay){
gate(state, delay);
trig = kTrigger;
}
void setRetrigger(bool state){
retrigger = state;
}
void gate(bool state){
gate(state, 0);
}
void gate(bool state, int delay){
if(gateState != state){
gateTime = delay;
gateState = state;
}
trig = kGate;
}
float getLevel(){
return level;
}
void setLevel(float newLevel){
level = newLevel;
}
/**
* Produce the next envelope sample.
*/
float generate(){
if(gateTime == 0){
stage = kAttack;
if(trig == kTrigger){
gateState = false;
}
}
if(gateTime >= 0){
gateTime--; // this will stop at -1
}
switch (stage) {
case kAttack:
// attack ramp
level = increment(level, attackIncrement);
if(level >= 1.0){
level = 1.0;
stage = kDecay;
}else if(gateState == false && trig == kGate){
stage = kRelease;
}
break;
case kDecay:
// decay ramp
level = decrement(level, decayIncrement);
if(level <= sustain){
level = sustain;
if(trig == kGate){
stage = kSustain;
} else { // (trig == kTrigger)
stage = kRelease;
}
} else if(gateState == false && trig == kGate){
stage = kRelease;
}
break;
case kSustain:
level = sustain;
if(gateState == false){
stage = kRelease;
}
break;
case kRelease:
// release ramp
level = decrement(level, releaseIncrement);
if(level <= MINLEVEL){
level = MINLEVEL;
if (retrigger == true)
trigger();
else // (retrigger == false)
stage = kIdle;
}else if(gateState == true ){ // if during release the gate is on again, start over from the current level
stage = kAttack;
}
break;
case kIdle:
level = MINLEVEL;
if(gateState == true)
stage = kAttack;
break;
}
return level;
}
[[deprecated("use generate() instead.")]]
float getNextSample(){
return generate(); // increments envelope one step
}
[[deprecated("use generate() instead.")]]
void getEnvelope(FloatArray output){
generate(output); // increments envelope by output buffer length
}
[[deprecated("use process() instead.")]]
void attenuate(FloatArray buf){
process(buf, buf); // increments envelope by buffer length
}
static AdsrEnvelope<linear>* create(float sampleRate){
return new AdsrEnvelope<linear>(sampleRate);
}
static void destroy(AdsrEnvelope<linear>* env){
delete env;
}
protected:
float sampleRate;
EnvelopeStage stage;
EnvelopeTrigger trig;
bool retrigger;
float level;
float attackIncrement;
float decayIncrement;
float releaseIncrement;
float sustain;
bool gateState;
int gateTime;
};

template<>
float AdsrEnvelope<true>::increment(float level, float amount){
return level + amount;
}

template<>
float AdsrEnvelope<true>::decrement(float level, float amount){
return level + amount;
}

template<>
float AdsrEnvelope<false>::increment(float level, float amount){
return level + (1.01-level)*amount; // aim slightly higher to ensure we reach 1.0
}

template<>
float AdsrEnvelope<false>::decrement(float level, float amount){
return level + level * amount;
}

template<>
float AdsrEnvelope<true>::calculateIncrement(float startValue, float endValue, float time){
return (endValue-startValue)/(sampleRate*time+1);
}

template<>
float AdsrEnvelope<false>::calculateIncrement(float startValue, float endValue, float time) {
// Ref: Christian Schoenebeck http://www.musicdsp.org/showone.php?id=189
return (logf(endValue) - logf(startValue)) / (time*sampleRate+10);
}

template<>
const float AdsrEnvelope<true>::MINLEVEL = 0;

template<>
const float AdsrEnvelope<false>::MINLEVEL = 0.00001; // -100dB

typedef AdsrEnvelope<true> LinearAdsrEnvelope;
typedef AdsrEnvelope<false> ExponentialAdsrEnvelope;

#endif /* __ADSR_ENVELOPE_H */
14 changes: 9 additions & 5 deletions LibSource/AgnesiOscillator.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,31 @@ class AgnesiOscillator : public Oscillator {
float getPhase(){
return M_PI*x/N+M_PI;
}
void reset(){
x = -N;
}
/**
* Normalise offset and gain so that signal is between 0 and 1
* Normalise offset and gain so that signal is between -1 and 1
*/
void normalise(){
offset = agnesi(N, a);
gain = 1/(agnesi(0, a) - offset);
gain = 2/(agnesi(0, a) - offset);
offset += 0.5;
}
float generate(){
float y = agnesi(x, a);
x += incr;
if(x > N)
x -= 2*N;
return gain*(y-offset);
return clamp(gain*(y-offset), -1.0f, 1.0f);
}
float generate(float fm){
// modulate coefficient 'a' instead of rate
float y = agnesi(x, a+fm);
float y = agnesi(x, a*(1+fm));
x += incr;
if(x > N)
x -= 2*N;
return gain*(y-offset);
return clamp(gain*(y-offset), -1.0f, 1.0f);
}
using SignalGenerator::generate;
static AgnesiOscillator* create(float sr, float a=0.5, float N=5){
Expand Down
Loading