diff options
12 files changed, 1970 insertions, 811 deletions
diff --git a/maximilian.cpp b/maximilian.cpp index 7ee5868..7ba4978 100644 --- a/maximilian.cpp +++ b/maximilian.cpp @@ -39,7 +39,7 @@ * Uncomment the following to include Sean Barrett's Ogg Vorbis decoder. * If you're on windows, make sure to add the files std_vorbis.c and std_vorbis.h to your project*/ -#define VORBIS +//#define VORBIS #ifdef VORBIS extern "C" { @@ -541,6 +541,7 @@ bool maxiSample::loadOgg(string fileName, int channel) { readChannel=channel; int channelx; // cout << fileName << endl; + free(temp); myDataSize = stb_vorbis_decode_filename(const_cast<char*>(fileName.c_str()), &channelx, &temp); result = myDataSize > 0; printf("\nchannels = %d\nlength = %d",channelx,myDataSize); @@ -565,6 +566,7 @@ bool maxiSample::loadOgg(string fileName, int channel) { //This sets the playback position to the start of a sample void maxiSample::trigger() { position = 0; + recordPosition = 0; } //This is the main read function. @@ -572,8 +574,8 @@ bool maxiSample::read() { bool result; ifstream inFile( myPath.c_str(), ios::in | ios::binary); - result = (bool) inFile; - if (inFile) { + result = inFile.is_open(); + if (result) { bool datafound = false; inFile.seekg(4, ios::beg); inFile.read( (char*) &myChunkSize, 4 ); // read the ChunkSize @@ -617,7 +619,7 @@ bool maxiSample::read() } // read the data chunk - myData = (char*) malloc(myDataSize * sizeof(char)); + char * myData = (char*) malloc(myDataSize * sizeof(char)); inFile.seekg(filePos, ios::beg); inFile.read(myData, myDataSize); length=myDataSize*(0.5/myChannels); @@ -633,6 +635,7 @@ bool maxiSample::read() position+=2; } } + free(temp); temp = (short*) malloc(myDataSize * sizeof(char)); memcpy(temp, myData, myDataSize * sizeof(char)); @@ -651,11 +654,35 @@ bool maxiSample::read() //This plays back at the correct speed. Always loops. double maxiSample::play() { position++; - if ((long) position == length) position=0; + if ((long) position >= length) position=0; output = (double) temp[(long)position]/32767.0; return output; } +void maxiSample::setPosition(double newPos) { + position = maxiMap::clamp<double>(newPos, 0.0, 1.0) * length; +} + +//start end and points are between 0 and 1 +double maxiSample::playLoop(double start, double end) { + position++; + if (position < length * start) position = length * start; + if ((long) position >= length * end) position = length * start; + output = (double) temp[(long)position]/32767.0; + return output; +} + +double maxiSample::playUntil(double end) { + position++; + if ((long) position<length * end) + output = (double) temp[(long)position]/32767.0; + else { + output=0; + } + return output; +} + + //This plays back at the correct speed. Only plays once. To retrigger, you have to manually reset the position double maxiSample::playOnce() { position++; @@ -1055,7 +1082,7 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double void maxiSample::getLength() { - length=myDataSize*0.5; + length=myDataSize*0.5; } void maxiSample::setLength(unsigned long numSamples) { @@ -1073,13 +1100,76 @@ void maxiSample::setLength(unsigned long numSamples) { } void maxiSample::clear() { - memset(myData, 0, myDataSize); + memset(temp, 0, myDataSize); } void maxiSample::reset() { position=0; } +void maxiSample::normalise(float maxLevel) { + short maxValue = 0; + for(int i=0; i < length; i++) { + if (abs(temp[i]) > maxValue) { + maxValue = abs(temp[i]); + } + } + float scale = 32767.0 * maxLevel / (float) maxValue; + for(int i=0; i < length; i++) { + temp[i] = round(scale * (float) temp[i]); + } +} + +void maxiSample::autoTrim(float alpha, float threshold, bool trimStart, bool trimEnd) { + + int startMarker=0; + if(trimStart) { + maxiLagExp<float> startLag(alpha, 0); + while(startMarker < length) { + startLag.addSample(abs(temp[startMarker])); + if (startLag.value() > threshold) { + break; + } + startMarker++; + } + } + + int endMarker = length-1; + if(trimEnd) { + maxiLagExp<float> endLag(alpha, 0); + while(endMarker > 0) { + endLag.addSample(abs(temp[endMarker])); + if (endLag.value() > threshold) { + break; + } + endMarker--; + } + } + + cout << "Autotrim: start: " << startMarker << ", end: " << endMarker << endl; + + int newLength = endMarker - startMarker; + if (newLength > 0) { + short *newData = (short*) malloc(sizeof(short) * newLength); + for(int i=0; i < newLength; i++) { + newData[i] = temp[i+startMarker]; + } + free(temp); + temp = newData; + myDataSize = newLength * 2; + length=newLength; + position=0; + recordPosition=0; + //envelope the start + int fadeSize=min((long)100, length); + for(int i=0; i < fadeSize; i++) { + float factor = i / (float) fadeSize; + temp[i] = round(temp[i] * factor); + temp[length - 1 - i] = round(temp[length - 1 - i] * factor); + } + } +} + @@ -1381,11 +1471,11 @@ double convert::mtof(int midinote) { } -void maxiEnvelopeFollower::setAttack(double attackMS) { +template<> void maxiEnvelopeFollower::setAttack(double attackMS) { attack = pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); } -void maxiEnvelopeFollower::setRelease(double releaseMS) { +template<> void maxiEnvelopeFollower::setRelease(double releaseMS) { release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); } diff --git a/maximilian.h b/maximilian.h index b3146d4..8a78c26 100755 --- a/maximilian.h +++ b/maximilian.h @@ -227,20 +227,35 @@ public: void setLength(unsigned long numSamples); - char* myData; +// char* myData; short* temp; // get/set for the Path property ~maxiSample() { - if (myData) free(myData); +// if (myData) free(myData); if (temp) free(temp); printf("freeing SampleData"); } - maxiSample():myData(NULL),temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; + maxiSample():temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; + + maxiSample& operator=(const maxiSample &source) { + if (this == &source) + return *this; + position=0; + recordPosition = 0; + myChannels = source.myChannels; + mySampleRate = maxiSettings::sampleRate; + free(temp); + myDataSize = source.myDataSize; + temp = (short*) malloc(myDataSize * sizeof(char)); + memcpy(temp, source.temp, myDataSize * sizeof(char)); + length = source.length; + return *this; + } bool load(string fileName, int channel=0); @@ -254,70 +269,77 @@ public: //read an ogg file into this class using stb_vorbis bool readOgg(); - void loopRecord(double newSample, const bool recordEnabled, const double recordMix) { + void loopRecord(double newSample, const bool recordEnabled, const double recordMix, double start = 0.0, double end = 1.0) { loopRecordLag.addSample(recordEnabled); + if (recordPosition < start * length) recordPosition = start * length; if(recordEnabled) { - double currentSample = ((short*)myData)[(unsigned long)recordPosition] / 32767.0; + double currentSample = temp[(unsigned long)recordPosition] / 32767.0; newSample = (recordMix * currentSample) + ((1.0 - recordMix) * newSample); newSample *= loopRecordLag.value(); - ((short*)myData)[(unsigned long)recordPosition] = newSample * 32767; + temp[(unsigned long)recordPosition] = newSample * 32767; } ++recordPosition; - if (recordPosition == length) - recordPosition=0; + if (recordPosition >= end * length) + recordPosition= start * length; } void clear(); void reset(); - - double play(); - - double playOnce(); - - double playOnce(double speed); - - double play(double speed); - - double play(double frequency, double start, double end, double &pos); - - double play(double frequency, double start, double end); - - double play4(double frequency, double start, double end); - - double bufferPlay(unsigned char &bufferin,long length); - - double bufferPlay(unsigned char &bufferin,double speed,long length); - - double bufferPlay(unsigned char &bufferin,double frequency, double start, double end); - - double bufferPlay4(unsigned char &bufferin,double frequency, double start, double end); + + double play(); + + double playLoop(double start, double end); // start and end are between 0.0 and 1.0 + + double playOnce(); + + double playOnce(double speed); + + void setPosition(double newPos); // between 0.0 and 1.0 + + double playUntil(double end); + + double play(double speed); + + double play(double frequency, double start, double end, double &pos); + + double play(double frequency, double start, double end); + + double play4(double frequency, double start, double end); + + double bufferPlay(unsigned char &bufferin,long length); + + double bufferPlay(unsigned char &bufferin,double speed,long length); + + double bufferPlay(unsigned char &bufferin,double frequency, double start, double end); + + double bufferPlay4(unsigned char &bufferin,double frequency, double start, double end); bool save() { - save(myPath); + return save(myPath); } bool save(string filename) { - fstream myFile (filename.c_str(), ios::out | ios::binary); - - // write the wav file per the wav file format - myFile.seekp (0, ios::beg); - myFile.write ("RIFF", 4); - myFile.write ((char*) &myChunkSize, 4); - myFile.write ("WAVE", 4); - myFile.write ("fmt ", 4); - myFile.write ((char*) &mySubChunk1Size, 4); - myFile.write ((char*) &myFormat, 2); - myFile.write ((char*) &myChannels, 2); - myFile.write ((char*) &mySampleRate, 4); - myFile.write ((char*) &myByteRate, 4); - myFile.write ((char*) &myBlockAlign, 2); - myFile.write ((char*) &myBitsPerSample, 2); - myFile.write ("data", 4); - myFile.write ((char*) &myDataSize, 4); - myFile.write (myData, myDataSize); - - return true; + fstream myFile (filename.c_str(), ios::out | ios::binary); + + // write the wav file per the wav file format + myFile.seekp (0, ios::beg); + myFile.write ("RIFF", 4); + myFile.write ((char*) &myChunkSize, 4); + myFile.write ("WAVE", 4); + myFile.write ("fmt ", 4); + myFile.write ((char*) &mySubChunk1Size, 4); + myFile.write ((char*) &myFormat, 2); + myFile.write ((char*) &myChannels, 2); + myFile.write ((char*) &mySampleRate, 4); + myFile.write ((char*) &myByteRate, 4); + myFile.write ((char*) &myBlockAlign, 2); + myFile.write ((char*) &myBitsPerSample, 2); + myFile.write ("data", 4); + myFile.write ((char*) &myDataSize, 4); + myFile.write ((char*) temp, myDataSize); + + return true; } // return a printable summary of the wav file @@ -328,32 +350,41 @@ public: std::cout << myDataSize; return summary; } + + void normalise(float maxLevel = 0.99); //0 < maxLevel < 1.0 + void autoTrim(float alpha = 0.3, float threshold = 6000, bool trimStart = true, bool trimEnd = true); //alpha of lag filter (lower == slower reaction), threshold to mark start and end, < 32767 }; class maxiMap { public: - static double inline linlin(double val, double inMin, double inMax, double outMin, double outMax) { - return ((val - inMin) / (inMax - inMin) * (outMax - outMin)) + outMin; - } - - static double inline linexp(double val, double inMin, double inMax, double outMin, double outMax) { - //clipping - val = max(min(val, inMax), inMin); - return pow((outMax / outMin), (val - inMin) / (inMax - inMin)) * outMin; - } - - static double inline explin(double val, double inMin, double inMax, double outMin, double outMax) { - //clipping - val = max(min(val, inMax), inMin); - return (log(val/inMin) / log(inMax/inMin) * (outMax - outMin)) + outMin; - } - - static int inline clamp(int v, const int low, const int high) { - v = min(high, v); - v = max(low, v); - return v; - } + static double inline linlin(double val, double inMin, double inMax, double outMin, double outMax) { + val = max(min(val, inMax), inMin); + return ((val - inMin) / (inMax - inMin) * (outMax - outMin)) + outMin; + } + + static double inline linexp(double val, double inMin, double inMax, double outMin, double outMax) { + //clipping + val = max(min(val, inMax), inMin); + return pow((outMax / outMin), (val - inMin) / (inMax - inMin)) * outMin; + } + + static double inline explin(double val, double inMin, double inMax, double outMin, double outMax) { + //clipping + val = max(min(val, inMax), inMin); + return (log(val/inMin) / log(inMax/inMin) * (outMax - outMin)) + outMin; + } + + //changed to templated function, e.g. maxiMap::maxiClamp<int>(v, l, h); + template<typename T> + static T inline clamp(T v, const T low, const T high) { + if (v > high) + v = high; + else if (v < low) { + v = low; + } + return v; + } }; @@ -491,26 +522,115 @@ inline double maxiChorus::chorus(const double input, const unsigned int delay, c return (output1 + output2 + input) / 3.0; } -class maxiEnvelopeFollower { +template<typename T> +class maxiEnvelopeFollowerType { public: - maxiEnvelopeFollower() { + maxiEnvelopeFollowerType() { setAttack(100); setRelease(100); env = 0; } - void setAttack(double attackMS); - void setRelease(double releaseMS); - inline double play(double input) { + void setAttack(T attackMS) { + attack = pow( 0.01, 1.0 / (attackMS * maxiSettings::sampleRate * 0.001 ) ); + } + void setRelease(T releaseMS) { + release = pow( 0.01, 1.0 / (releaseMS * maxiSettings::sampleRate * 0.001 ) ); + } + inline T play(T input) { input = fabs(input); if (input>env) env = attack * (env - input) + input; else - env = release * (env - input) + input; + env = release * (env - input) + input; return env; } - void reset() {env=0;} + void reset() {env=0;} + inline T getEnv(){return env;} + inline void setEnv(T val){env = val;} +private: + T attack, release, env; +}; + +typedef maxiEnvelopeFollowerType<double> maxiEnvelopeFollower; +typedef maxiEnvelopeFollowerType<float> maxiEnvelopeFollowerF; + +class maxiDCBlocker { +public: + double xm1, ym1; + maxiDCBlocker() : xm1(0), ym1(0) {} + inline double play(double input, double R) { + ym1 = input - xm1 + R * ym1; + xm1 = input; + return ym1; + } +}; + +/* + State Variable Filter + + algorithm from http://www.cytomic.com/files/dsp/SvfLinearTrapOptimised.pdf + usage: + either set the parameters separately as required (to save CPU) + + filter.setCutoff(param1); + filter.setResonance(param2); + + w = filter.play(w, 0.0, 1.0, 0.0, 0.0); + + or set everything together at once + + w = filter.setCutoff(param1).setResonance(param2).play(w, 0.0, 1.0, 0.0, 0.0); + + */ +class maxiSVF { +public: + maxiSVF() : v0z(0), v1(0), v2(0) { setParams(1000, 1);} + + //20 < cutoff < 20000 + inline maxiSVF& setCutoff(double cutoff) { + setParams(cutoff, res); + return *this; + } + + //from 0 upwards, starts to ring from 2-3ish, cracks a bit around 10 + inline maxiSVF& setResonance(double q) { + setParams(freq, q); + return *this; + } + + //run the filter, and get a mixture of lowpass, bandpass, highpass and notch outputs + inline double play(double w, double lpmix, double bpmix, double hpmix, double notchmix) { + double low, band, high, notch; + double v1z = v1; + double v2z = v2; + double v3 = w + v0z - 2.0 * v2z; + v1 += g1*v3-g2*v1z; + v2 += g3*v3+g4*v1z; + v0z = w; + low = v2; + band = v1; + high = w-k*v1-v2; + notch = w-k*v1; + return (low * lpmix) + (band * bpmix) + (high * hpmix) + (notch * notchmix); + } + private: - double attack, release, env; + inline void setParams(double _freq, double _res) { + freq = _freq; + res = _res; + g = tan(PI * freq / maxiSettings::sampleRate); + damping = res == 0 ? 0 : 1.0 / res; + k = damping; + ginv = g / (1.0 + g * (g + k)); + g1 = ginv; + g2 = 2.0 * (g + k) * ginv; + g3 = g * ginv; + g4 = 2.0 * ginv; + } + + double v0z, v1, v2, g, damping, k, ginv, g1, g2, g3 ,g4; + double freq, res; + }; diff --git a/maximilianTest.xcodeproj/project.xcworkspace/xcuserdata/michaelgrierson.xcuserdatad/UserInterfaceState.xcuserstate b/maximilianTest.xcodeproj/project.xcworkspace/xcuserdata/michaelgrierson.xcuserdatad/UserInterfaceState.xcuserstate Binary files differindex 6ed211d..8959558 100644 --- a/maximilianTest.xcodeproj/project.xcworkspace/xcuserdata/michaelgrierson.xcuserdatad/UserInterfaceState.xcuserstate +++ b/maximilianTest.xcodeproj/project.xcworkspace/xcuserdata/michaelgrierson.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/maximilianTest.xcodeproj/xcuserdata/michaelgrierson.xcuserdatad/xcschemes/maximilianTest.xcscheme b/maximilianTest.xcodeproj/xcuserdata/michaelgrierson.xcuserdatad/xcschemes/maximilianTest.xcscheme index 51dce60..903750a 100644 --- a/maximilianTest.xcodeproj/xcuserdata/michaelgrierson.xcuserdatad/xcschemes/maximilianTest.xcscheme +++ b/maximilianTest.xcodeproj/xcuserdata/michaelgrierson.xcuserdatad/xcschemes/maximilianTest.xcscheme @@ -35,7 +35,7 @@ BlueprintIdentifier = "8DD76F620486A84900D96B5E" BuildableName = "maximilianTest" BlueprintName = "maximilianTest" - ReferencedContainer = "container:maximilianTest_10.8.xcodeproj"> + ReferencedContainer = "container:maximilianTest.xcodeproj"> </BuildableReference> </MacroExpansion> </TestAction> @@ -55,7 +55,7 @@ BlueprintIdentifier = "8DD76F620486A84900D96B5E" BuildableName = "maximilianTest" BlueprintName = "maximilianTest" - ReferencedContainer = "container:maximilianTest_10.8.xcodeproj"> + ReferencedContainer = "container:maximilianTest.xcodeproj"> </BuildableReference> </BuildableProductRunnable> <AdditionalOptions> diff --git a/ofxMaxim/ofxMaxim/libs/maximilian.cpp b/ofxMaxim/ofxMaxim/libs/maximilian.cpp index 18b2af2..7ba4978 100644 --- a/ofxMaxim/ofxMaxim/libs/maximilian.cpp +++ b/ofxMaxim/ofxMaxim/libs/maximilian.cpp @@ -325,19 +325,6 @@ double maxiOsc::saw(double frequency) { phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); -} - -double maxiOsc::triangle(double frequency) { - //This is a triangle wave. - if ( phase >= 1.0 ) phase -= 1.0; - phase += (1./(maxiSettings::sampleRate/(frequency))); - if (phase <= 0.5 ) { - output =(phase - 0.25) * 4; - } else { - output =((1.0-phase) - 0.25) * 4; - } - return(output); - } double maxiOsc::sawn(double frequency) { @@ -359,12 +346,29 @@ double maxiOsc::sawn(double frequency) { } +double maxiOsc::rect(double frequency, double duty) { + + return (output); +} + +double maxiOsc::triangle(double frequency) { + //This is a triangle wave. + if ( phase >= 1.0 ) phase -= 1.0; + phase += (1./(maxiSettings::sampleRate/(frequency))); + if (phase <= 0.5 ) { + output =(phase - 0.25) * 4; + } else { + output =((1.0-phase) - 0.25) * 4; + } + return(output); + +} double maxiEnvelope::line(int numberofsegments,double segments[1000]) { //This is a basic multi-segment ramp generator that you can use for more or less anything. //However, it's not that intuitive. if (isPlaying==1) {//only make a sound once you've been triggered - period=2./(segments[valindex+1]*0.004); + period=4./(segments[valindex+1]*0.0044); nextval=segments[valindex+2]; currentval=segments[valindex]; if (currentval-amplitude > 0.0000001 && valindex < numberofsegments) { @@ -397,8 +401,7 @@ void maxiEnvelope::trigger(int index, double amp) { //Delay with feedback maxiDelayline::maxiDelayline() { - memset( memory, 0, 88200*sizeof (double) ); - phase=0; + memset( memory, 0, 88200*sizeof (double) ); } @@ -437,9 +440,9 @@ double maxiFilter::hipass(double input, double cutoff) { } //awesome. cuttof is freq in hz. res is between 1 and whatever. Watch out! double maxiFilter::lores(double input,double cutoff1, double resonance) { - cutoff=cutoff1*0.5; + cutoff=cutoff1; if (cutoff<10) cutoff=10; - if (cutoff>(maxiSettings::sampleRate*0.5)) cutoff=(maxiSettings::sampleRate*0.5); + if (cutoff>(maxiSettings::sampleRate)) cutoff=(maxiSettings::sampleRate); if (resonance<1.) resonance = 1.; z=cos(TWOPI*cutoff/maxiSettings::sampleRate); c=2-2*z; @@ -453,9 +456,9 @@ double maxiFilter::lores(double input,double cutoff1, double resonance) { //working hires filter double maxiFilter::hires(double input,double cutoff1, double resonance) { - cutoff=cutoff1*0.5; + cutoff=cutoff1; if (cutoff<10) cutoff=10; - if (cutoff>(maxiSettings::sampleRate*0.5)) cutoff=(maxiSettings::sampleRate*0.5); + if (cutoff>(maxiSettings::sampleRate)) cutoff=(maxiSettings::sampleRate); if (resonance<1.) resonance = 1.; z=cos(TWOPI*cutoff/maxiSettings::sampleRate); c=2-2*z; @@ -524,18 +527,20 @@ double *maxiMix::ambisonic(double input,double eight[8],double x,double y,double return(eight); } - +//This is the maxiSample load function. It just calls read. bool maxiSample::load(string fileName, int channel) { myPath = fileName; readChannel=channel; return read(); } +// This is for OGG loading bool maxiSample::loadOgg(string fileName, int channel) { #ifdef VORBIS bool result; readChannel=channel; int channelx; +// cout << fileName << endl; free(temp); myDataSize = stb_vorbis_decode_filename(const_cast<char*>(fileName.c_str()), &channelx, &temp); result = myDataSize > 0; @@ -558,11 +563,13 @@ bool maxiSample::loadOgg(string fileName, int channel) { return 0; } +//This sets the playback position to the start of a sample void maxiSample::trigger() { position = 0; recordPosition = 0; } +//This is the main read function. bool maxiSample::read() { bool result; @@ -612,7 +619,7 @@ bool maxiSample::read() } // read the data chunk - char *myData = (char*) malloc(myDataSize * sizeof(char)); + char * myData = (char*) malloc(myDataSize * sizeof(char)); inFile.seekg(filePos, ios::beg); inFile.read(myData, myDataSize); length=myDataSize*(0.5/myChannels); @@ -644,6 +651,7 @@ bool maxiSample::read() return result; // this should probably be something more descriptive } +//This plays back at the correct speed. Always loops. double maxiSample::play() { position++; if ((long) position >= length) position=0; @@ -651,40 +659,43 @@ double maxiSample::play() { return output; } +void maxiSample::setPosition(double newPos) { + position = maxiMap::clamp<double>(newPos, 0.0, 1.0) * length; +} + //start end and points are between 0 and 1 double maxiSample::playLoop(double start, double end) { - position++; + position++; if (position < length * start) position = length * start; - if ((long) position >= length * end) position = length * start; - output = (double) temp[(long)position]/32767.0; - return output; + if ((long) position >= length * end) position = length * start; + output = (double) temp[(long)position]/32767.0; + return output; } -double maxiSample::playOnce() { - position++; - if ((long) position<length) +double maxiSample::playUntil(double end) { + position++; + if ((long) position<length * end) output = (double) temp[(long)position]/32767.0; else { output=0; } - return output; - + return output; } -void maxiSample::setPosition(double newPos) { - position = maxiMap::clamp<double>(newPos, 0.0, 1.0) * length; -} -double maxiSample::playUntil(double end) { +//This plays back at the correct speed. Only plays once. To retrigger, you have to manually reset the position +double maxiSample::playOnce() { position++; - if ((long) position<length * end) + if ((long) position<length) output = (double) temp[(long)position]/32767.0; else { output=0; } return output; + } +//Same as above but takes a speed value specified as a ratio, with 1.0 as original speed double maxiSample::playOnce(double speed) { position=position+((speed*chandiv)/(maxiSettings::sampleRate/mySampleRate)); double remainder = position - (long) position; @@ -695,6 +706,7 @@ double maxiSample::playOnce(double speed) { return(output); } +//As above but looping double maxiSample::play(double speed) { double remainder; long a,b; @@ -740,10 +752,12 @@ double maxiSample::play(double speed) { return(output); } +//placeholder double maxiSample::play(double frequency, double start, double end) { return play(frequency, start, end, position); } +//This allows you to say how often a second you want a specific chunk of audio to play double maxiSample::play(double frequency, double start, double end, double &pos) { double remainder; if (end>=length) end=length-1; @@ -755,7 +769,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) } if ( pos >= end ) pos = start; - pos += ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); + pos += ((end-start)/((maxiSettings::sampleRate)/(frequency*chandiv))); remainder = pos - floor(pos); long posl = floor(pos); if (posl+1<length) { @@ -775,7 +789,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) output = (double) ((1-remainder) * temp[a] + remainder * temp[b])/32767;//linear interpolation } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( pos <= start ) pos = end; pos -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = pos - floor(pos); @@ -801,7 +815,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) } -//better cubic inerpolation. Cobbled together from various (pd externals, yehar, other places). +//Same as above. better cubic inerpolation. Cobbled together from various (pd externals, yehar, other places). double maxiSample::play4(double frequency, double start, double end) { double remainder; double a,b,c,d,a1,a2,a3; @@ -840,7 +854,7 @@ double maxiSample::play4(double frequency, double start, double end) { output = (double) (((a3 * remainder + a2) * remainder + a1) * remainder + b) / 32767; } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -876,6 +890,8 @@ double maxiSample::play4(double frequency, double start, double end) { return(output); } + +//You don't need to worry about this stuff. double maxiSample::bufferPlay(unsigned char &bufferin,long length) { double remainder; short* buffer = (short *)&bufferin; @@ -963,7 +979,7 @@ double maxiSample::bufferPlay(unsigned char &bufferin,double frequency, double s output = (double) ((1-remainder) * buffer[a] + remainder * buffer[b])/32767;//linear interpolation } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -1028,7 +1044,7 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double output = (double) (((a3 * remainder + a2) * remainder + a1) * remainder + b) / 32767; } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -1066,18 +1082,17 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double void maxiSample::getLength() { - length=myDataSize*0.5; + length=myDataSize*0.5; } void maxiSample::setLength(unsigned long numSamples) { - temp = (short*) realloc(temp, sizeof(short) * numSamples); -// short *newData = (short*) malloc(sizeof(short) * numSamples); -// if (NULL!=temp) { -// unsigned long copyLength = min((unsigned long)length, numSamples); -// memcpy(newData, temp, sizeof(short) * copyLength); -// free(temp); -// } -// temp = newData; + cout << "Length: " << numSamples << endl; + short *newData = (short*) malloc(sizeof(short) * numSamples); + if (NULL!=temp) { + unsigned long copyLength = min((unsigned long)length, numSamples); + memcpy(newData, temp, sizeof(short) * copyLength); + } + temp = newData; myDataSize = numSamples * 2; length=numSamples; position=0; @@ -1092,7 +1107,6 @@ void maxiSample::reset() { position=0; } - void maxiSample::normalise(float maxLevel) { short maxValue = 0; for(int i=0; i < length; i++) { @@ -1160,10 +1174,6 @@ void maxiSample::autoTrim(float alpha, float threshold, bool trimStart, bool tri - - - - /* OK this compressor and gate are now ready to use. The envelopes, like all the envelopes in this recent update, use stupid algorithms for incrementing - consequently a long attack is something like 0.0001 and a long release is like 0.9999. Annoyingly, a short attack is 0.1, and a short release is 0.99. I'll sort this out laters */ @@ -1237,6 +1247,37 @@ double maxiDyn::compressor(double input, double ratio, double threshold, double return output*(1+log(ratio)); } +double maxiDyn::compress(double input) { + + if (fabs(input)>threshold && attackphase!=1){ + holdcount=0; + releasephase=0; + attackphase=1; + if(currentRatio==0) currentRatio=ratio; + } + + if (attackphase==1 && currentRatio<ratio-1) { + currentRatio*=(1+attack); + } + + if (currentRatio>=ratio-1) { + attackphase=0; + releasephase=1; + } + + if (releasephase==1 && currentRatio>0.) { + currentRatio*=release; + } + + if (input>0.) { + output = input/(1.+currentRatio); + } else { + output = input/(1.+currentRatio); + } + + return output*(1+log(ratio)); +} + /* Lots of people struggle with the envelope generators so here's a new easy one. It takes mental numbers for attack and release tho. Basically, they're exponentials. @@ -1282,61 +1323,159 @@ double maxiEnv::ar(double input, double attack, double release, long holdtime, i return output; } -/* and here's a new adsr. It's not bad, very simple to use*/ +/* adsr. It's not bad, very simple to use*/ double maxiEnv::adsr(double input, double attack, double decay, double sustain, double release, long holdtime, int trigger) { - - if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ - holdcount=0; - decayphase=0; - sustainphase=0; - releasephase=0; - attackphase=1; - } - - if (attackphase==1) { - amplitude+=(1*attack); - output=input*amplitude; - } - - if (amplitude>=1) { - amplitude=1; - attackphase=0; - decayphase=1; - } - - if (decayphase==1) { - output=input*(amplitude*=decay); - if (amplitude<=sustain) { - decayphase=0; - holdphase=1; - } - } - - if (holdcount<holdtime && holdphase==1) { - output=input*amplitude; - holdcount++; - } - - if (holdcount==holdtime && trigger==1) { - output=input*amplitude; - } - - if (holdcount==holdtime && trigger!=1) { - holdphase=0; - releasephase=1; - } - - if (releasephase==1 && amplitude>0.) { - output=input*(amplitude*=release); - - } - - return output; + + if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ + holdcount=0; + decayphase=0; + sustainphase=0; + releasephase=0; + attackphase=1; + } + + if (attackphase==1) { + releasephase=0; + amplitude+=(1*attack); + output=input*amplitude; + + if (amplitude>=1) { + amplitude=1; + attackphase=0; + decayphase=1; + } + } + + + if (decayphase==1) { + output=input*(amplitude*=decay); + if (amplitude<=sustain) { + decayphase=0; + holdphase=1; + } + } + + if (holdcount<holdtime && holdphase==1) { + output=input*amplitude; + holdcount++; + } + + if (holdcount>=holdtime && trigger==1) { + output=input*amplitude; + } + + if (holdcount>=holdtime && trigger!=1) { + holdphase=0; + releasephase=1; + } + + if (releasephase==1 && amplitude>0.) { + output=input*(amplitude*=release); + + } + + return output; } +double maxiEnv::adsr(double input, int trigger) { + + if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ + holdcount=0; + decayphase=0; + sustainphase=0; + releasephase=0; + attackphase=1; + } + + if (attackphase==1) { + releasephase=0; + amplitude+=(1*attack); + output=input*amplitude; + + if (amplitude>=1) { + amplitude=1; + attackphase=0; + decayphase=1; + } + } + + + if (decayphase==1) { + output=input*(amplitude*=decay); + if (amplitude<=sustain) { + decayphase=0; + holdphase=1; + } + } + + if (holdcount<holdtime && holdphase==1) { + output=input*amplitude; + holdcount++; + } + + if (holdcount>=holdtime && trigger==1) { + output=input*amplitude; + } + + if (holdcount>=holdtime && trigger!=1) { + holdphase=0; + releasephase=1; + } + + if (releasephase==1 && amplitude>0.) { + output=input*(amplitude*=release); + + } + + return output; +} + + +void maxiEnv::setAttack(double attackMS) { + attack = 1-pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiEnv::setRelease(double releaseMS) { + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiEnv::setSustain(double sustainL) { + sustain = sustainL; +} + +void maxiEnv::setDecay(double decayMS) { + decay = pow( 0.01, 1.0 / ( decayMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setAttack(double attackMS) { + attack = pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setRelease(double releaseMS) { + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setThreshold(double thresholdI) { + threshold = thresholdI; +} + +void maxiDyn::setRatio(double ratioF) { + ratio = ratioF; +} + + double convert::mtof(int midinote) { return mtofarray[midinote]; } + +template<> void maxiEnvelopeFollower::setAttack(double attackMS) { + attack = pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); +} + +template<> void maxiEnvelopeFollower::setRelease(double releaseMS) { + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); +} + diff --git a/ofxMaxim/ofxMaxim/libs/maximilian.h b/ofxMaxim/ofxMaxim/libs/maximilian.h index 03dfea5..8a78c26 100755 --- a/ofxMaxim/ofxMaxim/libs/maximilian.h +++ b/ofxMaxim/ofxMaxim/libs/maximilian.h @@ -30,12 +30,6 @@ * */ -/* - Feature request: - maxiNANINFAlarm - maxiLimiter - */ - #ifndef MAXIMILIAN_H #define MAXIMILIAN_H @@ -49,6 +43,10 @@ #include <cstdlib> #include "math.h" +#ifdef _WIN32 //|| _WIN64 +#include <algorithm> +#endif + using namespace std; #ifndef PI #define PI 3.1415926535897932384626433832795 @@ -91,8 +89,9 @@ public: double noise(); double sinebuf(double frequency); double sinebuf4(double frequency); + double sawn(double frequency); + double rect(double frequency, double duty=0.5); void phaseReset(double phaseIn); - double sawn(double frequency); }; @@ -214,12 +213,12 @@ private: int myByteRate; short myBlockAlign; short myBitsPerSample; + double position, recordPosition; double speed; double output; maxiLagExp<double> loopRecordLag; public: - double position, recordPosition; int myDataSize; short myChannels; int mySampleRate; @@ -237,9 +236,11 @@ public: { // if (myData) free(myData); if (temp) free(temp); + printf("freeing SampleData"); + } - maxiSample():temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; + maxiSample():temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; maxiSample& operator=(const maxiSample &source) { if (this == &source) @@ -285,60 +286,60 @@ public: void clear(); void reset(); - - double play(); - + + double play(); + double playLoop(double start, double end); // start and end are between 0.0 and 1.0 - - double playOnce(); - - double playOnce(double speed); - + + double playOnce(); + + double playOnce(double speed); + void setPosition(double newPos); // between 0.0 and 1.0 double playUntil(double end); - - double play(double speed); - - double play(double frequency, double start, double end, double &pos); - - double play(double frequency, double start, double end); - - double play4(double frequency, double start, double end); - - double bufferPlay(unsigned char &bufferin,long length); - - double bufferPlay(unsigned char &bufferin,double speed,long length); - - double bufferPlay(unsigned char &bufferin,double frequency, double start, double end); - - double bufferPlay4(unsigned char &bufferin,double frequency, double start, double end); + + double play(double speed); + + double play(double frequency, double start, double end, double &pos); + + double play(double frequency, double start, double end); + + double play4(double frequency, double start, double end); + + double bufferPlay(unsigned char &bufferin,long length); + + double bufferPlay(unsigned char &bufferin,double speed,long length); + + double bufferPlay(unsigned char &bufferin,double frequency, double start, double end); + + double bufferPlay4(unsigned char &bufferin,double frequency, double start, double end); bool save() { return save(myPath); } bool save(string filename) { - fstream myFile (filename.c_str(), ios::out | ios::binary); - - // write the wav file per the wav file format - myFile.seekp (0, ios::beg); - myFile.write ("RIFF", 4); - myFile.write ((char*) &myChunkSize, 4); - myFile.write ("WAVE", 4); - myFile.write ("fmt ", 4); - myFile.write ((char*) &mySubChunk1Size, 4); - myFile.write ((char*) &myFormat, 2); - myFile.write ((char*) &myChannels, 2); - myFile.write ((char*) &mySampleRate, 4); - myFile.write ((char*) &myByteRate, 4); - myFile.write ((char*) &myBlockAlign, 2); - myFile.write ((char*) &myBitsPerSample, 2); - myFile.write ("data", 4); - myFile.write ((char*) &myDataSize, 4); - myFile.write ((char*) temp, myDataSize); - - return true; + fstream myFile (filename.c_str(), ios::out | ios::binary); + + // write the wav file per the wav file format + myFile.seekp (0, ios::beg); + myFile.write ("RIFF", 4); + myFile.write ((char*) &myChunkSize, 4); + myFile.write ("WAVE", 4); + myFile.write ("fmt ", 4); + myFile.write ((char*) &mySubChunk1Size, 4); + myFile.write ((char*) &myFormat, 2); + myFile.write ((char*) &myChannels, 2); + myFile.write ((char*) &mySampleRate, 4); + myFile.write ((char*) &myByteRate, 4); + myFile.write ((char*) &myBlockAlign, 2); + myFile.write ((char*) &myBitsPerSample, 2); + myFile.write ("data", 4); + myFile.write ((char*) &myDataSize, 4); + myFile.write ((char*) temp, myDataSize); + + return true; } // return a printable summary of the wav file @@ -346,6 +347,7 @@ public: { char *summary = new char[250]; sprintf(summary, " Format: %d\n Channels: %d\n SampleRate: %d\n ByteRate: %d\n BlockAlign: %d\n BitsPerSample: %d\n DataSize: %d\n", myFormat, myChannels, mySampleRate, myByteRate, myBlockAlign, myBitsPerSample, myDataSize); + std::cout << myDataSize; return summary; } @@ -356,43 +358,47 @@ public: class maxiMap { public: - static double inline linlin(double val, double inMin, double inMax, double outMin, double outMax) { - val = max(min(val, inMax), inMin); - return ((val - inMin) / (inMax - inMin) * (outMax - outMin)) + outMin; - } - - static double inline linexp(double val, double inMin, double inMax, double outMin, double outMax) { - //clipping - val = max(min(val, inMax), inMin); - return pow((outMax / outMin), (val - inMin) / (inMax - inMin)) * outMin; - } - - static double inline explin(double val, double inMin, double inMax, double outMin, double outMax) { - //clipping - val = max(min(val, inMax), inMin); - return (log(val/inMin) / log(inMax/inMin) * (outMax - outMin)) + outMin; - } - + static double inline linlin(double val, double inMin, double inMax, double outMin, double outMax) { + val = max(min(val, inMax), inMin); + return ((val - inMin) / (inMax - inMin) * (outMax - outMin)) + outMin; + } + + static double inline linexp(double val, double inMin, double inMax, double outMin, double outMax) { + //clipping + val = max(min(val, inMax), inMin); + return pow((outMax / outMin), (val - inMin) / (inMax - inMin)) * outMin; + } + + static double inline explin(double val, double inMin, double inMax, double outMin, double outMax) { + //clipping + val = max(min(val, inMax), inMin); + return (log(val/inMin) / log(inMax/inMin) * (outMax - outMin)) + outMin; + } + //changed to templated function, e.g. maxiMap::maxiClamp<int>(v, l, h); template<typename T> - static T inline clamp(T v, const T low, const T high) { + static T inline clamp(T v, const T low, const T high) { if (v > high) v = high; else if (v < low) { v = low; } - return v; - } + return v; + } }; + class maxiDyn { public: - double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); - double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); - double input; +// double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); +// double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); + double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); + double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); + double compress(double input); + double input; double ratio; double currentRatio; double threshold; @@ -400,6 +406,10 @@ public: double attack; double release; double amplitude; + void setAttack(double attackMS); + void setRelease(double releaseMS); + void setThreshold(double thresholdI); + void setRatio(double ratioF); long holdtime; long holdcount; int attackphase,holdphase,releasephase; @@ -411,6 +421,7 @@ class maxiEnv { public: double ar(double input, double attack=1, double release=0.9, long holdtime=1, int trigger=0); double adsr(double input, double attack=1, double decay=0.99, double sustain=0.125, double release=0.9, long holdtime=1, int trigger=0); + double adsr(double input,int trigger); double input; double output; double attack; @@ -418,8 +429,12 @@ public: double sustain; double release; double amplitude; + void setAttack(double attackMS); + void setRelease(double releaseMS); + void setDecay(double decayMS); + void setSustain(double sustainL); int trigger; - long holdtime; + long holdtime=1; long holdcount; int attackphase,decayphase,sustainphase,holdphase,releasephase; }; @@ -516,20 +531,20 @@ public: env = 0; } void setAttack(T attackMS) { - attack = pow( 0.01, 1.0 / (attackMS * maxiSettings::sampleRate * 0.001 ) ); + attack = pow( 0.01, 1.0 / (attackMS * maxiSettings::sampleRate * 0.001 ) ); } void setRelease(T releaseMS) { - release = pow( 0.01, 1.0 / (releaseMS * maxiSettings::sampleRate * 0.001 ) ); + release = pow( 0.01, 1.0 / (releaseMS * maxiSettings::sampleRate * 0.001 ) ); } inline T play(T input) { input = fabs(input); if (input>env) env = attack * (env - input) + input; else - env = release * (env - input) + input; + env = release * (env - input) + input; return env; } - void reset() {env=0;} + void reset() {env=0;} inline T getEnv(){return env;} inline void setEnv(T val){env = val;} private: @@ -539,7 +554,6 @@ private: typedef maxiEnvelopeFollowerType<double> maxiEnvelopeFollower; typedef maxiEnvelopeFollowerType<float> maxiEnvelopeFollowerF; -//from https://ccrma.stanford.edu/~jos/filters/DC_Blocker_Software_Implementations.html class maxiDCBlocker { public: double xm1, ym1; diff --git a/ofxMaxim/openFrameworksExamples/OSX/OF0.8.4MaximExtractorExample/ofxMaxim/libs/maximilian.cpp b/ofxMaxim/openFrameworksExamples/OSX/OF0.8.4MaximExtractorExample/ofxMaxim/libs/maximilian.cpp index 18b2af2..7ba4978 100755..100644 --- a/ofxMaxim/openFrameworksExamples/OSX/OF0.8.4MaximExtractorExample/ofxMaxim/libs/maximilian.cpp +++ b/ofxMaxim/openFrameworksExamples/OSX/OF0.8.4MaximExtractorExample/ofxMaxim/libs/maximilian.cpp @@ -325,19 +325,6 @@ double maxiOsc::saw(double frequency) { phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); -} - -double maxiOsc::triangle(double frequency) { - //This is a triangle wave. - if ( phase >= 1.0 ) phase -= 1.0; - phase += (1./(maxiSettings::sampleRate/(frequency))); - if (phase <= 0.5 ) { - output =(phase - 0.25) * 4; - } else { - output =((1.0-phase) - 0.25) * 4; - } - return(output); - } double maxiOsc::sawn(double frequency) { @@ -359,12 +346,29 @@ double maxiOsc::sawn(double frequency) { } +double maxiOsc::rect(double frequency, double duty) { + + return (output); +} + +double maxiOsc::triangle(double frequency) { + //This is a triangle wave. + if ( phase >= 1.0 ) phase -= 1.0; + phase += (1./(maxiSettings::sampleRate/(frequency))); + if (phase <= 0.5 ) { + output =(phase - 0.25) * 4; + } else { + output =((1.0-phase) - 0.25) * 4; + } + return(output); + +} double maxiEnvelope::line(int numberofsegments,double segments[1000]) { //This is a basic multi-segment ramp generator that you can use for more or less anything. //However, it's not that intuitive. if (isPlaying==1) {//only make a sound once you've been triggered - period=2./(segments[valindex+1]*0.004); + period=4./(segments[valindex+1]*0.0044); nextval=segments[valindex+2]; currentval=segments[valindex]; if (currentval-amplitude > 0.0000001 && valindex < numberofsegments) { @@ -397,8 +401,7 @@ void maxiEnvelope::trigger(int index, double amp) { //Delay with feedback maxiDelayline::maxiDelayline() { - memset( memory, 0, 88200*sizeof (double) ); - phase=0; + memset( memory, 0, 88200*sizeof (double) ); } @@ -437,9 +440,9 @@ double maxiFilter::hipass(double input, double cutoff) { } //awesome. cuttof is freq in hz. res is between 1 and whatever. Watch out! double maxiFilter::lores(double input,double cutoff1, double resonance) { - cutoff=cutoff1*0.5; + cutoff=cutoff1; if (cutoff<10) cutoff=10; - if (cutoff>(maxiSettings::sampleRate*0.5)) cutoff=(maxiSettings::sampleRate*0.5); + if (cutoff>(maxiSettings::sampleRate)) cutoff=(maxiSettings::sampleRate); if (resonance<1.) resonance = 1.; z=cos(TWOPI*cutoff/maxiSettings::sampleRate); c=2-2*z; @@ -453,9 +456,9 @@ double maxiFilter::lores(double input,double cutoff1, double resonance) { //working hires filter double maxiFilter::hires(double input,double cutoff1, double resonance) { - cutoff=cutoff1*0.5; + cutoff=cutoff1; if (cutoff<10) cutoff=10; - if (cutoff>(maxiSettings::sampleRate*0.5)) cutoff=(maxiSettings::sampleRate*0.5); + if (cutoff>(maxiSettings::sampleRate)) cutoff=(maxiSettings::sampleRate); if (resonance<1.) resonance = 1.; z=cos(TWOPI*cutoff/maxiSettings::sampleRate); c=2-2*z; @@ -524,18 +527,20 @@ double *maxiMix::ambisonic(double input,double eight[8],double x,double y,double return(eight); } - +//This is the maxiSample load function. It just calls read. bool maxiSample::load(string fileName, int channel) { myPath = fileName; readChannel=channel; return read(); } +// This is for OGG loading bool maxiSample::loadOgg(string fileName, int channel) { #ifdef VORBIS bool result; readChannel=channel; int channelx; +// cout << fileName << endl; free(temp); myDataSize = stb_vorbis_decode_filename(const_cast<char*>(fileName.c_str()), &channelx, &temp); result = myDataSize > 0; @@ -558,11 +563,13 @@ bool maxiSample::loadOgg(string fileName, int channel) { return 0; } +//This sets the playback position to the start of a sample void maxiSample::trigger() { position = 0; recordPosition = 0; } +//This is the main read function. bool maxiSample::read() { bool result; @@ -612,7 +619,7 @@ bool maxiSample::read() } // read the data chunk - char *myData = (char*) malloc(myDataSize * sizeof(char)); + char * myData = (char*) malloc(myDataSize * sizeof(char)); inFile.seekg(filePos, ios::beg); inFile.read(myData, myDataSize); length=myDataSize*(0.5/myChannels); @@ -644,6 +651,7 @@ bool maxiSample::read() return result; // this should probably be something more descriptive } +//This plays back at the correct speed. Always loops. double maxiSample::play() { position++; if ((long) position >= length) position=0; @@ -651,40 +659,43 @@ double maxiSample::play() { return output; } +void maxiSample::setPosition(double newPos) { + position = maxiMap::clamp<double>(newPos, 0.0, 1.0) * length; +} + //start end and points are between 0 and 1 double maxiSample::playLoop(double start, double end) { - position++; + position++; if (position < length * start) position = length * start; - if ((long) position >= length * end) position = length * start; - output = (double) temp[(long)position]/32767.0; - return output; + if ((long) position >= length * end) position = length * start; + output = (double) temp[(long)position]/32767.0; + return output; } -double maxiSample::playOnce() { - position++; - if ((long) position<length) +double maxiSample::playUntil(double end) { + position++; + if ((long) position<length * end) output = (double) temp[(long)position]/32767.0; else { output=0; } - return output; - + return output; } -void maxiSample::setPosition(double newPos) { - position = maxiMap::clamp<double>(newPos, 0.0, 1.0) * length; -} -double maxiSample::playUntil(double end) { +//This plays back at the correct speed. Only plays once. To retrigger, you have to manually reset the position +double maxiSample::playOnce() { position++; - if ((long) position<length * end) + if ((long) position<length) output = (double) temp[(long)position]/32767.0; else { output=0; } return output; + } +//Same as above but takes a speed value specified as a ratio, with 1.0 as original speed double maxiSample::playOnce(double speed) { position=position+((speed*chandiv)/(maxiSettings::sampleRate/mySampleRate)); double remainder = position - (long) position; @@ -695,6 +706,7 @@ double maxiSample::playOnce(double speed) { return(output); } +//As above but looping double maxiSample::play(double speed) { double remainder; long a,b; @@ -740,10 +752,12 @@ double maxiSample::play(double speed) { return(output); } +//placeholder double maxiSample::play(double frequency, double start, double end) { return play(frequency, start, end, position); } +//This allows you to say how often a second you want a specific chunk of audio to play double maxiSample::play(double frequency, double start, double end, double &pos) { double remainder; if (end>=length) end=length-1; @@ -755,7 +769,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) } if ( pos >= end ) pos = start; - pos += ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); + pos += ((end-start)/((maxiSettings::sampleRate)/(frequency*chandiv))); remainder = pos - floor(pos); long posl = floor(pos); if (posl+1<length) { @@ -775,7 +789,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) output = (double) ((1-remainder) * temp[a] + remainder * temp[b])/32767;//linear interpolation } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( pos <= start ) pos = end; pos -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = pos - floor(pos); @@ -801,7 +815,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) } -//better cubic inerpolation. Cobbled together from various (pd externals, yehar, other places). +//Same as above. better cubic inerpolation. Cobbled together from various (pd externals, yehar, other places). double maxiSample::play4(double frequency, double start, double end) { double remainder; double a,b,c,d,a1,a2,a3; @@ -840,7 +854,7 @@ double maxiSample::play4(double frequency, double start, double end) { output = (double) (((a3 * remainder + a2) * remainder + a1) * remainder + b) / 32767; } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -876,6 +890,8 @@ double maxiSample::play4(double frequency, double start, double end) { return(output); } + +//You don't need to worry about this stuff. double maxiSample::bufferPlay(unsigned char &bufferin,long length) { double remainder; short* buffer = (short *)&bufferin; @@ -963,7 +979,7 @@ double maxiSample::bufferPlay(unsigned char &bufferin,double frequency, double s output = (double) ((1-remainder) * buffer[a] + remainder * buffer[b])/32767;//linear interpolation } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -1028,7 +1044,7 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double output = (double) (((a3 * remainder + a2) * remainder + a1) * remainder + b) / 32767; } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -1066,18 +1082,17 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double void maxiSample::getLength() { - length=myDataSize*0.5; + length=myDataSize*0.5; } void maxiSample::setLength(unsigned long numSamples) { - temp = (short*) realloc(temp, sizeof(short) * numSamples); -// short *newData = (short*) malloc(sizeof(short) * numSamples); -// if (NULL!=temp) { -// unsigned long copyLength = min((unsigned long)length, numSamples); -// memcpy(newData, temp, sizeof(short) * copyLength); -// free(temp); -// } -// temp = newData; + cout << "Length: " << numSamples << endl; + short *newData = (short*) malloc(sizeof(short) * numSamples); + if (NULL!=temp) { + unsigned long copyLength = min((unsigned long)length, numSamples); + memcpy(newData, temp, sizeof(short) * copyLength); + } + temp = newData; myDataSize = numSamples * 2; length=numSamples; position=0; @@ -1092,7 +1107,6 @@ void maxiSample::reset() { position=0; } - void maxiSample::normalise(float maxLevel) { short maxValue = 0; for(int i=0; i < length; i++) { @@ -1160,10 +1174,6 @@ void maxiSample::autoTrim(float alpha, float threshold, bool trimStart, bool tri - - - - /* OK this compressor and gate are now ready to use. The envelopes, like all the envelopes in this recent update, use stupid algorithms for incrementing - consequently a long attack is something like 0.0001 and a long release is like 0.9999. Annoyingly, a short attack is 0.1, and a short release is 0.99. I'll sort this out laters */ @@ -1237,6 +1247,37 @@ double maxiDyn::compressor(double input, double ratio, double threshold, double return output*(1+log(ratio)); } +double maxiDyn::compress(double input) { + + if (fabs(input)>threshold && attackphase!=1){ + holdcount=0; + releasephase=0; + attackphase=1; + if(currentRatio==0) currentRatio=ratio; + } + + if (attackphase==1 && currentRatio<ratio-1) { + currentRatio*=(1+attack); + } + + if (currentRatio>=ratio-1) { + attackphase=0; + releasephase=1; + } + + if (releasephase==1 && currentRatio>0.) { + currentRatio*=release; + } + + if (input>0.) { + output = input/(1.+currentRatio); + } else { + output = input/(1.+currentRatio); + } + + return output*(1+log(ratio)); +} + /* Lots of people struggle with the envelope generators so here's a new easy one. It takes mental numbers for attack and release tho. Basically, they're exponentials. @@ -1282,61 +1323,159 @@ double maxiEnv::ar(double input, double attack, double release, long holdtime, i return output; } -/* and here's a new adsr. It's not bad, very simple to use*/ +/* adsr. It's not bad, very simple to use*/ double maxiEnv::adsr(double input, double attack, double decay, double sustain, double release, long holdtime, int trigger) { - - if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ - holdcount=0; - decayphase=0; - sustainphase=0; - releasephase=0; - attackphase=1; - } - - if (attackphase==1) { - amplitude+=(1*attack); - output=input*amplitude; - } - - if (amplitude>=1) { - amplitude=1; - attackphase=0; - decayphase=1; - } - - if (decayphase==1) { - output=input*(amplitude*=decay); - if (amplitude<=sustain) { - decayphase=0; - holdphase=1; - } - } - - if (holdcount<holdtime && holdphase==1) { - output=input*amplitude; - holdcount++; - } - - if (holdcount==holdtime && trigger==1) { - output=input*amplitude; - } - - if (holdcount==holdtime && trigger!=1) { - holdphase=0; - releasephase=1; - } - - if (releasephase==1 && amplitude>0.) { - output=input*(amplitude*=release); - - } - - return output; + + if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ + holdcount=0; + decayphase=0; + sustainphase=0; + releasephase=0; + attackphase=1; + } + + if (attackphase==1) { + releasephase=0; + amplitude+=(1*attack); + output=input*amplitude; + + if (amplitude>=1) { + amplitude=1; + attackphase=0; + decayphase=1; + } + } + + + if (decayphase==1) { + output=input*(amplitude*=decay); + if (amplitude<=sustain) { + decayphase=0; + holdphase=1; + } + } + + if (holdcount<holdtime && holdphase==1) { + output=input*amplitude; + holdcount++; + } + + if (holdcount>=holdtime && trigger==1) { + output=input*amplitude; + } + + if (holdcount>=holdtime && trigger!=1) { + holdphase=0; + releasephase=1; + } + + if (releasephase==1 && amplitude>0.) { + output=input*(amplitude*=release); + + } + + return output; } +double maxiEnv::adsr(double input, int trigger) { + + if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ + holdcount=0; + decayphase=0; + sustainphase=0; + releasephase=0; + attackphase=1; + } + + if (attackphase==1) { + releasephase=0; + amplitude+=(1*attack); + output=input*amplitude; + + if (amplitude>=1) { + amplitude=1; + attackphase=0; + decayphase=1; + } + } + + + if (decayphase==1) { + output=input*(amplitude*=decay); + if (amplitude<=sustain) { + decayphase=0; + holdphase=1; + } + } + + if (holdcount<holdtime && holdphase==1) { + output=input*amplitude; + holdcount++; + } + + if (holdcount>=holdtime && trigger==1) { + output=input*amplitude; + } + + if (holdcount>=holdtime && trigger!=1) { + holdphase=0; + releasephase=1; + } + + if (releasephase==1 && amplitude>0.) { + output=input*(amplitude*=release); + + } + + return output; +} + + +void maxiEnv::setAttack(double attackMS) { + attack = 1-pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiEnv::setRelease(double releaseMS) { + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiEnv::setSustain(double sustainL) { + sustain = sustainL; +} + +void maxiEnv::setDecay(double decayMS) { + decay = pow( 0.01, 1.0 / ( decayMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setAttack(double attackMS) { + attack = pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setRelease(double releaseMS) { + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setThreshold(double thresholdI) { + threshold = thresholdI; +} + +void maxiDyn::setRatio(double ratioF) { + ratio = ratioF; +} + + double convert::mtof(int midinote) { return mtofarray[midinote]; } + +template<> void maxiEnvelopeFollower::setAttack(double attackMS) { + attack = pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); +} + +template<> void maxiEnvelopeFollower::setRelease(double releaseMS) { + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); +} + diff --git a/ofxMaxim/openFrameworksExamples/OSX/OF0.8.4MaximExtractorExample/ofxMaxim/libs/maximilian.h b/ofxMaxim/openFrameworksExamples/OSX/OF0.8.4MaximExtractorExample/ofxMaxim/libs/maximilian.h index 03dfea5..8a78c26 100755 --- a/ofxMaxim/openFrameworksExamples/OSX/OF0.8.4MaximExtractorExample/ofxMaxim/libs/maximilian.h +++ b/ofxMaxim/openFrameworksExamples/OSX/OF0.8.4MaximExtractorExample/ofxMaxim/libs/maximilian.h @@ -30,12 +30,6 @@ * */ -/* - Feature request: - maxiNANINFAlarm - maxiLimiter - */ - #ifndef MAXIMILIAN_H #define MAXIMILIAN_H @@ -49,6 +43,10 @@ #include <cstdlib> #include "math.h" +#ifdef _WIN32 //|| _WIN64 +#include <algorithm> +#endif + using namespace std; #ifndef PI #define PI 3.1415926535897932384626433832795 @@ -91,8 +89,9 @@ public: double noise(); double sinebuf(double frequency); double sinebuf4(double frequency); + double sawn(double frequency); + double rect(double frequency, double duty=0.5); void phaseReset(double phaseIn); - double sawn(double frequency); }; @@ -214,12 +213,12 @@ private: int myByteRate; short myBlockAlign; short myBitsPerSample; + double position, recordPosition; double speed; double output; maxiLagExp<double> loopRecordLag; public: - double position, recordPosition; int myDataSize; short myChannels; int mySampleRate; @@ -237,9 +236,11 @@ public: { // if (myData) free(myData); if (temp) free(temp); + printf("freeing SampleData"); + } - maxiSample():temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; + maxiSample():temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; maxiSample& operator=(const maxiSample &source) { if (this == &source) @@ -285,60 +286,60 @@ public: void clear(); void reset(); - - double play(); - + + double play(); + double playLoop(double start, double end); // start and end are between 0.0 and 1.0 - - double playOnce(); - - double playOnce(double speed); - + + double playOnce(); + + double playOnce(double speed); + void setPosition(double newPos); // between 0.0 and 1.0 double playUntil(double end); - - double play(double speed); - - double play(double frequency, double start, double end, double &pos); - - double play(double frequency, double start, double end); - - double play4(double frequency, double start, double end); - - double bufferPlay(unsigned char &bufferin,long length); - - double bufferPlay(unsigned char &bufferin,double speed,long length); - - double bufferPlay(unsigned char &bufferin,double frequency, double start, double end); - - double bufferPlay4(unsigned char &bufferin,double frequency, double start, double end); + + double play(double speed); + + double play(double frequency, double start, double end, double &pos); + + double play(double frequency, double start, double end); + + double play4(double frequency, double start, double end); + + double bufferPlay(unsigned char &bufferin,long length); + + double bufferPlay(unsigned char &bufferin,double speed,long length); + + double bufferPlay(unsigned char &bufferin,double frequency, double start, double end); + + double bufferPlay4(unsigned char &bufferin,double frequency, double start, double end); bool save() { return save(myPath); } bool save(string filename) { - fstream myFile (filename.c_str(), ios::out | ios::binary); - - // write the wav file per the wav file format - myFile.seekp (0, ios::beg); - myFile.write ("RIFF", 4); - myFile.write ((char*) &myChunkSize, 4); - myFile.write ("WAVE", 4); - myFile.write ("fmt ", 4); - myFile.write ((char*) &mySubChunk1Size, 4); - myFile.write ((char*) &myFormat, 2); - myFile.write ((char*) &myChannels, 2); - myFile.write ((char*) &mySampleRate, 4); - myFile.write ((char*) &myByteRate, 4); - myFile.write ((char*) &myBlockAlign, 2); - myFile.write ((char*) &myBitsPerSample, 2); - myFile.write ("data", 4); - myFile.write ((char*) &myDataSize, 4); - myFile.write ((char*) temp, myDataSize); - - return true; + fstream myFile (filename.c_str(), ios::out | ios::binary); + + // write the wav file per the wav file format + myFile.seekp (0, ios::beg); + myFile.write ("RIFF", 4); + myFile.write ((char*) &myChunkSize, 4); + myFile.write ("WAVE", 4); + myFile.write ("fmt ", 4); + myFile.write ((char*) &mySubChunk1Size, 4); + myFile.write ((char*) &myFormat, 2); + myFile.write ((char*) &myChannels, 2); + myFile.write ((char*) &mySampleRate, 4); + myFile.write ((char*) &myByteRate, 4); + myFile.write ((char*) &myBlockAlign, 2); + myFile.write ((char*) &myBitsPerSample, 2); + myFile.write ("data", 4); + myFile.write ((char*) &myDataSize, 4); + myFile.write ((char*) temp, myDataSize); + + return true; } // return a printable summary of the wav file @@ -346,6 +347,7 @@ public: { char *summary = new char[250]; sprintf(summary, " Format: %d\n Channels: %d\n SampleRate: %d\n ByteRate: %d\n BlockAlign: %d\n BitsPerSample: %d\n DataSize: %d\n", myFormat, myChannels, mySampleRate, myByteRate, myBlockAlign, myBitsPerSample, myDataSize); + std::cout << myDataSize; return summary; } @@ -356,43 +358,47 @@ public: class maxiMap { public: - static double inline linlin(double val, double inMin, double inMax, double outMin, double outMax) { - val = max(min(val, inMax), inMin); - return ((val - inMin) / (inMax - inMin) * (outMax - outMin)) + outMin; - } - - static double inline linexp(double val, double inMin, double inMax, double outMin, double outMax) { - //clipping - val = max(min(val, inMax), inMin); - return pow((outMax / outMin), (val - inMin) / (inMax - inMin)) * outMin; - } - - static double inline explin(double val, double inMin, double inMax, double outMin, double outMax) { - //clipping - val = max(min(val, inMax), inMin); - return (log(val/inMin) / log(inMax/inMin) * (outMax - outMin)) + outMin; - } - + static double inline linlin(double val, double inMin, double inMax, double outMin, double outMax) { + val = max(min(val, inMax), inMin); + return ((val - inMin) / (inMax - inMin) * (outMax - outMin)) + outMin; + } + + static double inline linexp(double val, double inMin, double inMax, double outMin, double outMax) { + //clipping + val = max(min(val, inMax), inMin); + return pow((outMax / outMin), (val - inMin) / (inMax - inMin)) * outMin; + } + + static double inline explin(double val, double inMin, double inMax, double outMin, double outMax) { + //clipping + val = max(min(val, inMax), inMin); + return (log(val/inMin) / log(inMax/inMin) * (outMax - outMin)) + outMin; + } + //changed to templated function, e.g. maxiMap::maxiClamp<int>(v, l, h); template<typename T> - static T inline clamp(T v, const T low, const T high) { + static T inline clamp(T v, const T low, const T high) { if (v > high) v = high; else if (v < low) { v = low; } - return v; - } + return v; + } }; + class maxiDyn { public: - double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); - double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); - double input; +// double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); +// double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); + double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); + double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); + double compress(double input); + double input; double ratio; double currentRatio; double threshold; @@ -400,6 +406,10 @@ public: double attack; double release; double amplitude; + void setAttack(double attackMS); + void setRelease(double releaseMS); + void setThreshold(double thresholdI); + void setRatio(double ratioF); long holdtime; long holdcount; int attackphase,holdphase,releasephase; @@ -411,6 +421,7 @@ class maxiEnv { public: double ar(double input, double attack=1, double release=0.9, long holdtime=1, int trigger=0); double adsr(double input, double attack=1, double decay=0.99, double sustain=0.125, double release=0.9, long holdtime=1, int trigger=0); + double adsr(double input,int trigger); double input; double output; double attack; @@ -418,8 +429,12 @@ public: double sustain; double release; double amplitude; + void setAttack(double attackMS); + void setRelease(double releaseMS); + void setDecay(double decayMS); + void setSustain(double sustainL); int trigger; - long holdtime; + long holdtime=1; long holdcount; int attackphase,decayphase,sustainphase,holdphase,releasephase; }; @@ -516,20 +531,20 @@ public: env = 0; } void setAttack(T attackMS) { - attack = pow( 0.01, 1.0 / (attackMS * maxiSettings::sampleRate * 0.001 ) ); + attack = pow( 0.01, 1.0 / (attackMS * maxiSettings::sampleRate * 0.001 ) ); } void setRelease(T releaseMS) { - release = pow( 0.01, 1.0 / (releaseMS * maxiSettings::sampleRate * 0.001 ) ); + release = pow( 0.01, 1.0 / (releaseMS * maxiSettings::sampleRate * 0.001 ) ); } inline T play(T input) { input = fabs(input); if (input>env) env = attack * (env - input) + input; else - env = release * (env - input) + input; + env = release * (env - input) + input; return env; } - void reset() {env=0;} + void reset() {env=0;} inline T getEnv(){return env;} inline void setEnv(T val){env = val;} private: @@ -539,7 +554,6 @@ private: typedef maxiEnvelopeFollowerType<double> maxiEnvelopeFollower; typedef maxiEnvelopeFollowerType<float> maxiEnvelopeFollowerF; -//from https://ccrma.stanford.edu/~jos/filters/DC_Blocker_Software_Implementations.html class maxiDCBlocker { public: double xm1, ym1; diff --git a/ofxMaxim/openFrameworksExamples/OSX/ofxMaximExample_0.8.4_OSX_Granular/ofxMaxim/libs/maximilian.cpp b/ofxMaxim/openFrameworksExamples/OSX/ofxMaximExample_0.8.4_OSX_Granular/ofxMaxim/libs/maximilian.cpp index 0b8ba86..7ba4978 100755..100644 --- a/ofxMaxim/openFrameworksExamples/OSX/ofxMaximExample_0.8.4_OSX_Granular/ofxMaxim/libs/maximilian.cpp +++ b/ofxMaxim/openFrameworksExamples/OSX/ofxMaximExample_0.8.4_OSX_Granular/ofxMaxim/libs/maximilian.cpp @@ -325,19 +325,6 @@ double maxiOsc::saw(double frequency) { phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); -} - -double maxiOsc::triangle(double frequency) { - //This is a triangle wave. - if ( phase >= 1.0 ) phase -= 1.0; - phase += (1./(maxiSettings::sampleRate/(frequency))); - if (phase <= 0.5 ) { - output =(phase - 0.25) * 4; - } else { - output =((1.0-phase) - 0.25) * 4; - } - return(output); - } double maxiOsc::sawn(double frequency) { @@ -359,12 +346,29 @@ double maxiOsc::sawn(double frequency) { } +double maxiOsc::rect(double frequency, double duty) { + + return (output); +} + +double maxiOsc::triangle(double frequency) { + //This is a triangle wave. + if ( phase >= 1.0 ) phase -= 1.0; + phase += (1./(maxiSettings::sampleRate/(frequency))); + if (phase <= 0.5 ) { + output =(phase - 0.25) * 4; + } else { + output =((1.0-phase) - 0.25) * 4; + } + return(output); + +} double maxiEnvelope::line(int numberofsegments,double segments[1000]) { //This is a basic multi-segment ramp generator that you can use for more or less anything. //However, it's not that intuitive. if (isPlaying==1) {//only make a sound once you've been triggered - period=2./(segments[valindex+1]*0.004); + period=4./(segments[valindex+1]*0.0044); nextval=segments[valindex+2]; currentval=segments[valindex]; if (currentval-amplitude > 0.0000001 && valindex < numberofsegments) { @@ -397,8 +401,7 @@ void maxiEnvelope::trigger(int index, double amp) { //Delay with feedback maxiDelayline::maxiDelayline() { - memset( memory, 0, 88200*sizeof (double) ); - phase=0; + memset( memory, 0, 88200*sizeof (double) ); } @@ -437,9 +440,9 @@ double maxiFilter::hipass(double input, double cutoff) { } //awesome. cuttof is freq in hz. res is between 1 and whatever. Watch out! double maxiFilter::lores(double input,double cutoff1, double resonance) { - cutoff=cutoff1*0.5; + cutoff=cutoff1; if (cutoff<10) cutoff=10; - if (cutoff>(maxiSettings::sampleRate*0.5)) cutoff=(maxiSettings::sampleRate*0.5); + if (cutoff>(maxiSettings::sampleRate)) cutoff=(maxiSettings::sampleRate); if (resonance<1.) resonance = 1.; z=cos(TWOPI*cutoff/maxiSettings::sampleRate); c=2-2*z; @@ -453,9 +456,9 @@ double maxiFilter::lores(double input,double cutoff1, double resonance) { //working hires filter double maxiFilter::hires(double input,double cutoff1, double resonance) { - cutoff=cutoff1*0.5; + cutoff=cutoff1; if (cutoff<10) cutoff=10; - if (cutoff>(maxiSettings::sampleRate*0.5)) cutoff=(maxiSettings::sampleRate*0.5); + if (cutoff>(maxiSettings::sampleRate)) cutoff=(maxiSettings::sampleRate); if (resonance<1.) resonance = 1.; z=cos(TWOPI*cutoff/maxiSettings::sampleRate); c=2-2*z; @@ -524,18 +527,20 @@ double *maxiMix::ambisonic(double input,double eight[8],double x,double y,double return(eight); } - +//This is the maxiSample load function. It just calls read. bool maxiSample::load(string fileName, int channel) { myPath = fileName; readChannel=channel; return read(); } +// This is for OGG loading bool maxiSample::loadOgg(string fileName, int channel) { #ifdef VORBIS bool result; readChannel=channel; int channelx; +// cout << fileName << endl; free(temp); myDataSize = stb_vorbis_decode_filename(const_cast<char*>(fileName.c_str()), &channelx, &temp); result = myDataSize > 0; @@ -558,11 +563,13 @@ bool maxiSample::loadOgg(string fileName, int channel) { return 0; } +//This sets the playback position to the start of a sample void maxiSample::trigger() { position = 0; recordPosition = 0; } +//This is the main read function. bool maxiSample::read() { bool result; @@ -597,7 +604,7 @@ bool maxiSample::read() //ignore any extra chunks char chunkID[5]=""; chunkID[4] = 0; - int filePos = 36; + int filePos = 20 + mySubChunk1Size; while(!datafound && !inFile.eof()) { inFile.seekg(filePos, ios::beg); inFile.read((char*) &chunkID, sizeof(char) * 4); @@ -612,7 +619,7 @@ bool maxiSample::read() } // read the data chunk - char *myData = (char*) malloc(myDataSize * sizeof(char)); + char * myData = (char*) malloc(myDataSize * sizeof(char)); inFile.seekg(filePos, ios::beg); inFile.read(myData, myDataSize); length=myDataSize*(0.5/myChannels); @@ -644,6 +651,7 @@ bool maxiSample::read() return result; // this should probably be something more descriptive } +//This plays back at the correct speed. Always loops. double maxiSample::play() { position++; if ((long) position >= length) position=0; @@ -651,40 +659,43 @@ double maxiSample::play() { return output; } +void maxiSample::setPosition(double newPos) { + position = maxiMap::clamp<double>(newPos, 0.0, 1.0) * length; +} + //start end and points are between 0 and 1 double maxiSample::playLoop(double start, double end) { - position++; + position++; if (position < length * start) position = length * start; - if ((long) position >= length * end) position = length * start; - output = (double) temp[(long)position]/32767.0; - return output; + if ((long) position >= length * end) position = length * start; + output = (double) temp[(long)position]/32767.0; + return output; } -double maxiSample::playOnce() { - position++; - if ((long) position<length) +double maxiSample::playUntil(double end) { + position++; + if ((long) position<length * end) output = (double) temp[(long)position]/32767.0; else { output=0; } - return output; - + return output; } -void maxiSample::setPosition(double newPos) { - position = maxiMap::clamp<double>(newPos, 0.0, 1.0) * length; -} -double maxiSample::playUntil(double end) { +//This plays back at the correct speed. Only plays once. To retrigger, you have to manually reset the position +double maxiSample::playOnce() { position++; - if ((long) position<length * end) + if ((long) position<length) output = (double) temp[(long)position]/32767.0; else { output=0; } return output; + } +//Same as above but takes a speed value specified as a ratio, with 1.0 as original speed double maxiSample::playOnce(double speed) { position=position+((speed*chandiv)/(maxiSettings::sampleRate/mySampleRate)); double remainder = position - (long) position; @@ -695,6 +706,7 @@ double maxiSample::playOnce(double speed) { return(output); } +//As above but looping double maxiSample::play(double speed) { double remainder; long a,b; @@ -740,10 +752,12 @@ double maxiSample::play(double speed) { return(output); } +//placeholder double maxiSample::play(double frequency, double start, double end) { return play(frequency, start, end, position); } +//This allows you to say how often a second you want a specific chunk of audio to play double maxiSample::play(double frequency, double start, double end, double &pos) { double remainder; if (end>=length) end=length-1; @@ -755,7 +769,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) } if ( pos >= end ) pos = start; - pos += ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); + pos += ((end-start)/((maxiSettings::sampleRate)/(frequency*chandiv))); remainder = pos - floor(pos); long posl = floor(pos); if (posl+1<length) { @@ -775,7 +789,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) output = (double) ((1-remainder) * temp[a] + remainder * temp[b])/32767;//linear interpolation } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( pos <= start ) pos = end; pos -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = pos - floor(pos); @@ -801,7 +815,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) } -//better cubic inerpolation. Cobbled together from various (pd externals, yehar, other places). +//Same as above. better cubic inerpolation. Cobbled together from various (pd externals, yehar, other places). double maxiSample::play4(double frequency, double start, double end) { double remainder; double a,b,c,d,a1,a2,a3; @@ -840,7 +854,7 @@ double maxiSample::play4(double frequency, double start, double end) { output = (double) (((a3 * remainder + a2) * remainder + a1) * remainder + b) / 32767; } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -876,6 +890,8 @@ double maxiSample::play4(double frequency, double start, double end) { return(output); } + +//You don't need to worry about this stuff. double maxiSample::bufferPlay(unsigned char &bufferin,long length) { double remainder; short* buffer = (short *)&bufferin; @@ -963,7 +979,7 @@ double maxiSample::bufferPlay(unsigned char &bufferin,double frequency, double s output = (double) ((1-remainder) * buffer[a] + remainder * buffer[b])/32767;//linear interpolation } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -1028,7 +1044,7 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double output = (double) (((a3 * remainder + a2) * remainder + a1) * remainder + b) / 32767; } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -1066,18 +1082,17 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double void maxiSample::getLength() { - length=myDataSize*0.5; + length=myDataSize*0.5; } void maxiSample::setLength(unsigned long numSamples) { - temp = (short*) realloc(temp, sizeof(short) * numSamples); -// short *newData = (short*) malloc(sizeof(short) * numSamples); -// if (NULL!=temp) { -// unsigned long copyLength = min((unsigned long)length, numSamples); -// memcpy(newData, temp, sizeof(short) * copyLength); -// free(temp); -// } -// temp = newData; + cout << "Length: " << numSamples << endl; + short *newData = (short*) malloc(sizeof(short) * numSamples); + if (NULL!=temp) { + unsigned long copyLength = min((unsigned long)length, numSamples); + memcpy(newData, temp, sizeof(short) * copyLength); + } + temp = newData; myDataSize = numSamples * 2; length=numSamples; position=0; @@ -1092,7 +1107,6 @@ void maxiSample::reset() { position=0; } - void maxiSample::normalise(float maxLevel) { short maxValue = 0; for(int i=0; i < length; i++) { @@ -1160,10 +1174,6 @@ void maxiSample::autoTrim(float alpha, float threshold, bool trimStart, bool tri - - - - /* OK this compressor and gate are now ready to use. The envelopes, like all the envelopes in this recent update, use stupid algorithms for incrementing - consequently a long attack is something like 0.0001 and a long release is like 0.9999. Annoyingly, a short attack is 0.1, and a short release is 0.99. I'll sort this out laters */ @@ -1237,6 +1247,37 @@ double maxiDyn::compressor(double input, double ratio, double threshold, double return output*(1+log(ratio)); } +double maxiDyn::compress(double input) { + + if (fabs(input)>threshold && attackphase!=1){ + holdcount=0; + releasephase=0; + attackphase=1; + if(currentRatio==0) currentRatio=ratio; + } + + if (attackphase==1 && currentRatio<ratio-1) { + currentRatio*=(1+attack); + } + + if (currentRatio>=ratio-1) { + attackphase=0; + releasephase=1; + } + + if (releasephase==1 && currentRatio>0.) { + currentRatio*=release; + } + + if (input>0.) { + output = input/(1.+currentRatio); + } else { + output = input/(1.+currentRatio); + } + + return output*(1+log(ratio)); +} + /* Lots of people struggle with the envelope generators so here's a new easy one. It takes mental numbers for attack and release tho. Basically, they're exponentials. @@ -1282,61 +1323,159 @@ double maxiEnv::ar(double input, double attack, double release, long holdtime, i return output; } -/* and here's a new adsr. It's not bad, very simple to use*/ +/* adsr. It's not bad, very simple to use*/ double maxiEnv::adsr(double input, double attack, double decay, double sustain, double release, long holdtime, int trigger) { - - if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ - holdcount=0; - decayphase=0; - sustainphase=0; - releasephase=0; - attackphase=1; - } - - if (attackphase==1) { - amplitude+=(1*attack); - output=input*amplitude; - } - - if (amplitude>=1) { - amplitude=1; - attackphase=0; - decayphase=1; - } - - if (decayphase==1) { - output=input*(amplitude*=decay); - if (amplitude<=sustain) { - decayphase=0; - holdphase=1; - } - } - - if (holdcount<holdtime && holdphase==1) { - output=input*amplitude; - holdcount++; - } - - if (holdcount==holdtime && trigger==1) { - output=input*amplitude; - } - - if (holdcount==holdtime && trigger!=1) { - holdphase=0; - releasephase=1; - } - - if (releasephase==1 && amplitude>0.) { - output=input*(amplitude*=release); - - } - - return output; + + if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ + holdcount=0; + decayphase=0; + sustainphase=0; + releasephase=0; + attackphase=1; + } + + if (attackphase==1) { + releasephase=0; + amplitude+=(1*attack); + output=input*amplitude; + + if (amplitude>=1) { + amplitude=1; + attackphase=0; + decayphase=1; + } + } + + + if (decayphase==1) { + output=input*(amplitude*=decay); + if (amplitude<=sustain) { + decayphase=0; + holdphase=1; + } + } + + if (holdcount<holdtime && holdphase==1) { + output=input*amplitude; + holdcount++; + } + + if (holdcount>=holdtime && trigger==1) { + output=input*amplitude; + } + + if (holdcount>=holdtime && trigger!=1) { + holdphase=0; + releasephase=1; + } + + if (releasephase==1 && amplitude>0.) { + output=input*(amplitude*=release); + + } + + return output; } +double maxiEnv::adsr(double input, int trigger) { + + if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ + holdcount=0; + decayphase=0; + sustainphase=0; + releasephase=0; + attackphase=1; + } + + if (attackphase==1) { + releasephase=0; + amplitude+=(1*attack); + output=input*amplitude; + + if (amplitude>=1) { + amplitude=1; + attackphase=0; + decayphase=1; + } + } + + + if (decayphase==1) { + output=input*(amplitude*=decay); + if (amplitude<=sustain) { + decayphase=0; + holdphase=1; + } + } + + if (holdcount<holdtime && holdphase==1) { + output=input*amplitude; + holdcount++; + } + + if (holdcount>=holdtime && trigger==1) { + output=input*amplitude; + } + + if (holdcount>=holdtime && trigger!=1) { + holdphase=0; + releasephase=1; + } + + if (releasephase==1 && amplitude>0.) { + output=input*(amplitude*=release); + + } + + return output; +} + + +void maxiEnv::setAttack(double attackMS) { + attack = 1-pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiEnv::setRelease(double releaseMS) { + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiEnv::setSustain(double sustainL) { + sustain = sustainL; +} + +void maxiEnv::setDecay(double decayMS) { + decay = pow( 0.01, 1.0 / ( decayMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setAttack(double attackMS) { + attack = pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setRelease(double releaseMS) { + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setThreshold(double thresholdI) { + threshold = thresholdI; +} + +void maxiDyn::setRatio(double ratioF) { + ratio = ratioF; +} + + double convert::mtof(int midinote) { return mtofarray[midinote]; } + +template<> void maxiEnvelopeFollower::setAttack(double attackMS) { + attack = pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); +} + +template<> void maxiEnvelopeFollower::setRelease(double releaseMS) { + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); +} + diff --git a/ofxMaxim/openFrameworksExamples/OSX/ofxMaximExample_0.8.4_OSX_Granular/ofxMaxim/libs/maximilian.h b/ofxMaxim/openFrameworksExamples/OSX/ofxMaximExample_0.8.4_OSX_Granular/ofxMaxim/libs/maximilian.h index 03dfea5..8a78c26 100755 --- a/ofxMaxim/openFrameworksExamples/OSX/ofxMaximExample_0.8.4_OSX_Granular/ofxMaxim/libs/maximilian.h +++ b/ofxMaxim/openFrameworksExamples/OSX/ofxMaximExample_0.8.4_OSX_Granular/ofxMaxim/libs/maximilian.h @@ -30,12 +30,6 @@ * */ -/* - Feature request: - maxiNANINFAlarm - maxiLimiter - */ - #ifndef MAXIMILIAN_H #define MAXIMILIAN_H @@ -49,6 +43,10 @@ #include <cstdlib> #include "math.h" +#ifdef _WIN32 //|| _WIN64 +#include <algorithm> +#endif + using namespace std; #ifndef PI #define PI 3.1415926535897932384626433832795 @@ -91,8 +89,9 @@ public: double noise(); double sinebuf(double frequency); double sinebuf4(double frequency); + double sawn(double frequency); + double rect(double frequency, double duty=0.5); void phaseReset(double phaseIn); - double sawn(double frequency); }; @@ -214,12 +213,12 @@ private: int myByteRate; short myBlockAlign; short myBitsPerSample; + double position, recordPosition; double speed; double output; maxiLagExp<double> loopRecordLag; public: - double position, recordPosition; int myDataSize; short myChannels; int mySampleRate; @@ -237,9 +236,11 @@ public: { // if (myData) free(myData); if (temp) free(temp); + printf("freeing SampleData"); + } - maxiSample():temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; + maxiSample():temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; maxiSample& operator=(const maxiSample &source) { if (this == &source) @@ -285,60 +286,60 @@ public: void clear(); void reset(); - - double play(); - + + double play(); + double playLoop(double start, double end); // start and end are between 0.0 and 1.0 - - double playOnce(); - - double playOnce(double speed); - + + double playOnce(); + + double playOnce(double speed); + void setPosition(double newPos); // between 0.0 and 1.0 double playUntil(double end); - - double play(double speed); - - double play(double frequency, double start, double end, double &pos); - - double play(double frequency, double start, double end); - - double play4(double frequency, double start, double end); - - double bufferPlay(unsigned char &bufferin,long length); - - double bufferPlay(unsigned char &bufferin,double speed,long length); - - double bufferPlay(unsigned char &bufferin,double frequency, double start, double end); - - double bufferPlay4(unsigned char &bufferin,double frequency, double start, double end); + + double play(double speed); + + double play(double frequency, double start, double end, double &pos); + + double play(double frequency, double start, double end); + + double play4(double frequency, double start, double end); + + double bufferPlay(unsigned char &bufferin,long length); + + double bufferPlay(unsigned char &bufferin,double speed,long length); + + double bufferPlay(unsigned char &bufferin,double frequency, double start, double end); + + double bufferPlay4(unsigned char &bufferin,double frequency, double start, double end); bool save() { return save(myPath); } bool save(string filename) { - fstream myFile (filename.c_str(), ios::out | ios::binary); - - // write the wav file per the wav file format - myFile.seekp (0, ios::beg); - myFile.write ("RIFF", 4); - myFile.write ((char*) &myChunkSize, 4); - myFile.write ("WAVE", 4); - myFile.write ("fmt ", 4); - myFile.write ((char*) &mySubChunk1Size, 4); - myFile.write ((char*) &myFormat, 2); - myFile.write ((char*) &myChannels, 2); - myFile.write ((char*) &mySampleRate, 4); - myFile.write ((char*) &myByteRate, 4); - myFile.write ((char*) &myBlockAlign, 2); - myFile.write ((char*) &myBitsPerSample, 2); - myFile.write ("data", 4); - myFile.write ((char*) &myDataSize, 4); - myFile.write ((char*) temp, myDataSize); - - return true; + fstream myFile (filename.c_str(), ios::out | ios::binary); + + // write the wav file per the wav file format + myFile.seekp (0, ios::beg); + myFile.write ("RIFF", 4); + myFile.write ((char*) &myChunkSize, 4); + myFile.write ("WAVE", 4); + myFile.write ("fmt ", 4); + myFile.write ((char*) &mySubChunk1Size, 4); + myFile.write ((char*) &myFormat, 2); + myFile.write ((char*) &myChannels, 2); + myFile.write ((char*) &mySampleRate, 4); + myFile.write ((char*) &myByteRate, 4); + myFile.write ((char*) &myBlockAlign, 2); + myFile.write ((char*) &myBitsPerSample, 2); + myFile.write ("data", 4); + myFile.write ((char*) &myDataSize, 4); + myFile.write ((char*) temp, myDataSize); + + return true; } // return a printable summary of the wav file @@ -346,6 +347,7 @@ public: { char *summary = new char[250]; sprintf(summary, " Format: %d\n Channels: %d\n SampleRate: %d\n ByteRate: %d\n BlockAlign: %d\n BitsPerSample: %d\n DataSize: %d\n", myFormat, myChannels, mySampleRate, myByteRate, myBlockAlign, myBitsPerSample, myDataSize); + std::cout << myDataSize; return summary; } @@ -356,43 +358,47 @@ public: class maxiMap { public: - static double inline linlin(double val, double inMin, double inMax, double outMin, double outMax) { - val = max(min(val, inMax), inMin); - return ((val - inMin) / (inMax - inMin) * (outMax - outMin)) + outMin; - } - - static double inline linexp(double val, double inMin, double inMax, double outMin, double outMax) { - //clipping - val = max(min(val, inMax), inMin); - return pow((outMax / outMin), (val - inMin) / (inMax - inMin)) * outMin; - } - - static double inline explin(double val, double inMin, double inMax, double outMin, double outMax) { - //clipping - val = max(min(val, inMax), inMin); - return (log(val/inMin) / log(inMax/inMin) * (outMax - outMin)) + outMin; - } - + static double inline linlin(double val, double inMin, double inMax, double outMin, double outMax) { + val = max(min(val, inMax), inMin); + return ((val - inMin) / (inMax - inMin) * (outMax - outMin)) + outMin; + } + + static double inline linexp(double val, double inMin, double inMax, double outMin, double outMax) { + //clipping + val = max(min(val, inMax), inMin); + return pow((outMax / outMin), (val - inMin) / (inMax - inMin)) * outMin; + } + + static double inline explin(double val, double inMin, double inMax, double outMin, double outMax) { + //clipping + val = max(min(val, inMax), inMin); + return (log(val/inMin) / log(inMax/inMin) * (outMax - outMin)) + outMin; + } + //changed to templated function, e.g. maxiMap::maxiClamp<int>(v, l, h); template<typename T> - static T inline clamp(T v, const T low, const T high) { + static T inline clamp(T v, const T low, const T high) { if (v > high) v = high; else if (v < low) { v = low; } - return v; - } + return v; + } }; + class maxiDyn { public: - double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); - double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); - double input; +// double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); +// double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); + double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); + double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); + double compress(double input); + double input; double ratio; double currentRatio; double threshold; @@ -400,6 +406,10 @@ public: double attack; double release; double amplitude; + void setAttack(double attackMS); + void setRelease(double releaseMS); + void setThreshold(double thresholdI); + void setRatio(double ratioF); long holdtime; long holdcount; int attackphase,holdphase,releasephase; @@ -411,6 +421,7 @@ class maxiEnv { public: double ar(double input, double attack=1, double release=0.9, long holdtime=1, int trigger=0); double adsr(double input, double attack=1, double decay=0.99, double sustain=0.125, double release=0.9, long holdtime=1, int trigger=0); + double adsr(double input,int trigger); double input; double output; double attack; @@ -418,8 +429,12 @@ public: double sustain; double release; double amplitude; + void setAttack(double attackMS); + void setRelease(double releaseMS); + void setDecay(double decayMS); + void setSustain(double sustainL); int trigger; - long holdtime; + long holdtime=1; long holdcount; int attackphase,decayphase,sustainphase,holdphase,releasephase; }; @@ -516,20 +531,20 @@ public: env = 0; } void setAttack(T attackMS) { - attack = pow( 0.01, 1.0 / (attackMS * maxiSettings::sampleRate * 0.001 ) ); + attack = pow( 0.01, 1.0 / (attackMS * maxiSettings::sampleRate * 0.001 ) ); } void setRelease(T releaseMS) { - release = pow( 0.01, 1.0 / (releaseMS * maxiSettings::sampleRate * 0.001 ) ); + release = pow( 0.01, 1.0 / (releaseMS * maxiSettings::sampleRate * 0.001 ) ); } inline T play(T input) { input = fabs(input); if (input>env) env = attack * (env - input) + input; else - env = release * (env - input) + input; + env = release * (env - input) + input; return env; } - void reset() {env=0;} + void reset() {env=0;} inline T getEnv(){return env;} inline void setEnv(T val){env = val;} private: @@ -539,7 +554,6 @@ private: typedef maxiEnvelopeFollowerType<double> maxiEnvelopeFollower; typedef maxiEnvelopeFollowerType<float> maxiEnvelopeFollowerF; -//from https://ccrma.stanford.edu/~jos/filters/DC_Blocker_Software_Implementations.html class maxiDCBlocker { public: double xm1, ym1; diff --git a/ofxMaxim/openFrameworksExamples/windows/ofxMaximExampleVS2010-OF0072/src/ofxMaxim/libs/maximilian.cpp b/ofxMaxim/openFrameworksExamples/windows/ofxMaximExampleVS2010-OF0072/src/ofxMaxim/libs/maximilian.cpp index bd19797..7ba4978 100644 --- a/ofxMaxim/openFrameworksExamples/windows/ofxMaximExampleVS2010-OF0072/src/ofxMaxim/libs/maximilian.cpp +++ b/ofxMaxim/openFrameworksExamples/windows/ofxMaximExampleVS2010-OF0072/src/ofxMaxim/libs/maximilian.cpp @@ -59,6 +59,142 @@ int maxiSettings::bufferSize = 1024; double sineBuffer[514]={0,0.012268,0.024536,0.036804,0.049042,0.06131,0.073547,0.085785,0.097992,0.1102,0.12241,0.13455,0.1467,0.15884,0.17093,0.18301,0.19507,0.20709,0.21909,0.23105,0.24295,0.25485,0.26669,0.2785,0.29025,0.30197,0.31366,0.32529,0.33685,0.34839,0.35986,0.37128,0.38266,0.39395,0.40521,0.41641,0.42752,0.4386,0.44958,0.46051,0.47137,0.48215,0.49286,0.50351,0.51407,0.52457,0.53497,0.54529,0.55554,0.5657,0.57578,0.58575,0.59567,0.60547,0.6152,0.62482,0.63437,0.6438,0.65314,0.66238,0.67151,0.68057,0.68951,0.69833,0.70706,0.7157,0.72421,0.7326,0.74091,0.74908,0.75717,0.76514,0.77298,0.7807,0.7883,0.79581,0.80316,0.81042,0.81754,0.82455,0.83142,0.8382,0.84482,0.85132,0.8577,0.86392,0.87006,0.87604,0.88187,0.8876,0.89319,0.89862,0.90396,0.90912,0.91415,0.91907,0.92383,0.92847,0.93295,0.93729,0.9415,0.94556,0.94949,0.95325,0.95691,0.96039,0.96375,0.96692,0.97,0.9729,0.97565,0.97827,0.98074,0.98306,0.98523,0.98724,0.98914,0.99084,0.99243,0.99387,0.99515,0.99628,0.99725,0.99808,0.99875,0.99927,0.99966,0.99988,0.99997,0.99988,0.99966,0.99927,0.99875,0.99808,0.99725,0.99628,0.99515,0.99387,0.99243,0.99084,0.98914,0.98724,0.98523,0.98306,0.98074,0.97827,0.97565,0.9729,0.97,0.96692,0.96375,0.96039,0.95691,0.95325,0.94949,0.94556,0.9415,0.93729,0.93295,0.92847,0.92383,0.91907,0.91415,0.90912,0.90396,0.89862,0.89319,0.8876,0.88187,0.87604,0.87006,0.86392,0.8577,0.85132,0.84482,0.8382,0.83142,0.82455,0.81754,0.81042,0.80316,0.79581,0.7883,0.7807,0.77298,0.76514,0.75717,0.74908,0.74091,0.7326,0.72421,0.7157,0.70706,0.69833,0.68951,0.68057,0.67151,0.66238,0.65314,0.6438,0.63437,0.62482,0.6152,0.60547,0.59567,0.58575,0.57578,0.5657,0.55554,0.54529,0.53497,0.52457,0.51407,0.50351,0.49286,0.48215,0.47137,0.46051,0.44958,0.4386,0.42752,0.41641,0.40521,0.39395,0.38266,0.37128,0.35986,0.34839,0.33685,0.32529,0.31366,0.30197,0.29025,0.2785,0.26669,0.25485,0.24295,0.23105,0.21909,0.20709,0.19507,0.18301,0.17093,0.15884,0.1467,0.13455,0.12241,0.1102,0.097992,0.085785,0.073547,0.06131,0.049042,0.036804,0.024536,0.012268,0,-0.012268,-0.024536,-0.036804,-0.049042,-0.06131,-0.073547,-0.085785,-0.097992,-0.1102,-0.12241,-0.13455,-0.1467,-0.15884,-0.17093,-0.18301,-0.19507,-0.20709,-0.21909,-0.23105,-0.24295,-0.25485,-0.26669,-0.2785,-0.29025,-0.30197,-0.31366,-0.32529,-0.33685,-0.34839,-0.35986,-0.37128,-0.38266,-0.39395,-0.40521,-0.41641,-0.42752,-0.4386,-0.44958,-0.46051,-0.47137,-0.48215,-0.49286,-0.50351,-0.51407,-0.52457,-0.53497,-0.54529,-0.55554,-0.5657,-0.57578,-0.58575,-0.59567,-0.60547,-0.6152,-0.62482,-0.63437,-0.6438,-0.65314,-0.66238,-0.67151,-0.68057,-0.68951,-0.69833,-0.70706,-0.7157,-0.72421,-0.7326,-0.74091,-0.74908,-0.75717,-0.76514,-0.77298,-0.7807,-0.7883,-0.79581,-0.80316,-0.81042,-0.81754,-0.82455,-0.83142,-0.8382,-0.84482,-0.85132,-0.8577,-0.86392,-0.87006,-0.87604,-0.88187,-0.8876,-0.89319,-0.89862,-0.90396,-0.90912,-0.91415,-0.91907,-0.92383,-0.92847,-0.93295,-0.93729,-0.9415,-0.94556,-0.94949,-0.95325,-0.95691,-0.96039,-0.96375,-0.96692,-0.97,-0.9729,-0.97565,-0.97827,-0.98074,-0.98306,-0.98523,-0.98724,-0.98914,-0.99084,-0.99243,-0.99387,-0.99515,-0.99628,-0.99725,-0.99808,-0.99875,-0.99927,-0.99966,-0.99988,-0.99997,-0.99988,-0.99966,-0.99927,-0.99875,-0.99808,-0.99725,-0.99628,-0.99515,-0.99387,-0.99243,-0.99084,-0.98914,-0.98724,-0.98523,-0.98306,-0.98074,-0.97827,-0.97565,-0.9729,-0.97,-0.96692,-0.96375,-0.96039,-0.95691,-0.95325,-0.94949,-0.94556,-0.9415,-0.93729,-0.93295,-0.92847,-0.92383,-0.91907,-0.91415,-0.90912,-0.90396,-0.89862,-0.89319,-0.8876,-0.88187,-0.87604,-0.87006,-0.86392,-0.8577,-0.85132,-0.84482,-0.8382,-0.83142,-0.82455,-0.81754,-0.81042,-0.80316,-0.79581,-0.7883,-0.7807,-0.77298,-0.76514,-0.75717,-0.74908,-0.74091,-0.7326,-0.72421,-0.7157,-0.70706,-0.69833,-0.68951,-0.68057,-0.67151,-0.66238,-0.65314,-0.6438,-0.63437,-0.62482,-0.6152,-0.60547,-0.59567,-0.58575,-0.57578,-0.5657,-0.55554,-0.54529,-0.53497,-0.52457,-0.51407,-0.50351,-0.49286,-0.48215,-0.47137,-0.46051,-0.44958,-0.4386,-0.42752,-0.41641,-0.40521,-0.39395,-0.38266,-0.37128,-0.35986,-0.34839,-0.33685,-0.32529,-0.31366,-0.30197,-0.29025,-0.2785,-0.26669,-0.25485,-0.24295,-0.23105,-0.21909,-0.20709,-0.19507,-0.18301,-0.17093,-0.15884,-0.1467,-0.13455,-0.12241,-0.1102,-0.097992,-0.085785,-0.073547,-0.06131,-0.049042,-0.036804,-0.024536,-0.012268,0,0.012268 }; +// This is a transition table that helps with bandlimited oscs. +double transition[1001]={-0.500003,-0.500003,-0.500023,-0.500063,-0.500121,-0.500179,-0.500259, + -0.50036,-0.500476,-0.500591,-0.500732,-0.500893,-0.501066,-0.501239, + -0.50144,-0.501661,-0.501891,-0.502123,-0.502382,-0.502662,-0.502949, + -0.50324,-0.503555,-0.503895,-0.504238,-0.504587,-0.504958,-0.505356, + -0.505754,-0.506162,-0.506589,-0.507042,-0.507495,-0.50796,-0.508444, + -0.508951,-0.509458,-0.509979,-0.510518,-0.511079,-0.511638,-0.512213, + -0.512808,-0.51342,-0.51403,-0.514659,-0.515307,-0.51597,-0.51663,-0.517312, + -0.518012,-0.518724,-0.519433,-0.520166,-0.520916,-0.521675,-0.522432, + -0.523214,-0.524013,-0.524819,-0.525624,-0.526451,-0.527298,-0.528147, + -0.528999,-0.52987,-0.530762,-0.531654,-0.532551,-0.533464,-0.534399, + -0.535332,-0.536271,-0.537226,-0.538202,-0.539172,-0.540152,-0.541148, + -0.542161,-0.543168,-0.544187,-0.54522,-0.546269,-0.54731,-0.548365, + -0.549434,-0.550516,-0.55159,-0.552679,-0.553781,-0.554893,-0.555997, + -0.557118,-0.558252,-0.559391,-0.560524,-0.561674,-0.562836,-0.564001, + -0.565161,-0.566336,-0.567524,-0.568712,-0.569896,-0.571095,-0.572306, + -0.573514,-0.574721,-0.575939,-0.577171,-0.578396,-0.579622,-0.580858, + -0.582108,-0.583348,-0.58459,-0.585842,-0.587106,-0.588358,-0.589614, + -0.590879,-0.592154,-0.593415,-0.594682,-0.595957,-0.59724,-0.598507, + -0.599782,-0.601064,-0.602351,-0.603623,-0.604902,-0.606189,-0.607476, + -0.60875,-0.610032,-0.611319,-0.612605,-0.613877,-0.615157,-0.616443, + -0.617723,-0.618992,-0.620268,-0.621548,-0.62282,-0.624083,-0.62535, + -0.626622,-0.627882,-0.629135,-0.630391,-0.631652,-0.632898,-0.634138, + -0.63538,-0.636626,-0.637854,-0.639078,-0.640304,-0.641531,-0.642739, + -0.643943,-0.645149,-0.646355,-0.647538,-0.64872,-0.649903,-0.651084, + -0.652241,-0.653397,-0.654553,-0.655705,-0.656834,-0.657961,-0.659087, + -0.660206,-0.661304,-0.662399,-0.663492,-0.664575,-0.665639,-0.666699, + -0.667756,-0.6688,-0.669827,-0.670849,-0.671866,-0.672868,-0.673854, + -0.674835,-0.675811,-0.676767,-0.677709,-0.678646,-0.679576,-0.680484, + -0.68138,-0.682269,-0.683151,-0.684008,-0.684854,-0.685693,-0.686524, + -0.687327,-0.688119,-0.688905,-0.689682,-0.690428,-0.691164,-0.691893, + -0.692613,-0.6933,-0.693978,-0.694647,-0.695305,-0.695932,-0.696549, + -0.697156,-0.697748,-0.698313,-0.698865,-0.699407,-0.699932,-0.700431, + -0.700917,-0.701391,-0.701845,-0.702276,-0.702693,-0.703097,-0.703478, + -0.703837,-0.704183,-0.704514,-0.704819,-0.705105,-0.705378,-0.705633, + -0.70586,-0.706069,-0.706265,-0.706444,-0.706591,-0.706721,-0.706837, + -0.706938,-0.707003,-0.707051,-0.707086,-0.707106,-0.707086,-0.707051, + -0.707001,-0.706935,-0.706832,-0.706711,-0.706576,-0.706421,-0.706233, + -0.706025,-0.705802,-0.705557,-0.705282,-0.704984,-0.704671,-0.704334, + -0.703969,-0.703582,-0.703176,-0.702746,-0.702288,-0.70181,-0.701312, + -0.700785,-0.700234,-0.699664,-0.69907,-0.698447,-0.6978,-0.697135, + -0.696446,-0.695725,-0.694981,-0.694219,-0.693435,-0.692613,-0.691771, + -0.690911,-0.69003,-0.689108,-0.688166,-0.687206,-0.686227,-0.685204, + -0.684162,-0.683101,-0.682019,-0.680898,-0.679755,-0.678592,-0.677407, + -0.676187,-0.674941,-0.673676,-0.672386,-0.671066,-0.669718,-0.66835, + -0.666955,-0.665532,-0.664083,-0.662611,-0.661112,-0.659585,-0.658035, + -0.656459,-0.654854,-0.653223,-0.651572,-0.649892,-0.648181,-0.646446, + -0.644691,-0.642909,-0.641093,-0.639253,-0.637393,-0.63551,-0.633588, + -0.631644,-0.62968,-0.627695,-0.625668,-0.623621,-0.621553,-0.619464, + -0.617334,-0.615183,-0.613011,-0.610817,-0.608587,-0.606333,-0.604058, + -0.60176,-0.599429,-0.597072,-0.594695,-0.592293,-0.589862,-0.587404, + -0.584925,-0.58242,-0.579888,-0.577331,-0.574751,-0.572145,-0.569512, + -0.566858,-0.564178,-0.561471,-0.558739,-0.555988,-0.553209,-0.550402, + -0.547572,-0.544723,-0.54185,-0.538944,-0.536018,-0.533072,-0.530105, + -0.527103,-0.524081,-0.52104,-0.51798,-0.514883,-0.511767,-0.508633, + -0.505479,-0.502291,-0.499083,-0.495857,-0.492611,-0.489335,-0.486037, + -0.48272,-0.479384,-0.476021,-0.472634,-0.46923,-0.465805,-0.462356, + -0.458884,-0.455394,-0.451882,-0.448348,-0.444795,-0.44122,-0.437624, + -0.434008,-0.430374,-0.426718,-0.423041,-0.419344,-0.415631,-0.411897, + -0.40814,-0.404365,-0.400575,-0.396766,-0.392933,-0.389082,-0.385217, + -0.381336,-0.377428,-0.373505,-0.369568,-0.365616,-0.361638,-0.357645, + -0.353638,-0.349617,-0.345572,-0.341512,-0.337438,-0.33335,-0.329242, + -0.325118,-0.32098,-0.316829,-0.31266,-0.308474,-0.304276,-0.300063, + -0.295836,-0.291593,-0.287337,-0.283067,-0.278783,-0.274487,-0.270176, + -0.265852,-0.261515,-0.257168,-0.252806,-0.248431,-0.244045,-0.239649, + -0.23524,-0.230817,-0.226385,-0.221943,-0.21749,-0.213024,-0.208548, + -0.204064,-0.199571,-0.195064,-0.190549,-0.186026,-0.181495,-0.176952, + -0.1724,-0.167842,-0.163277,-0.1587,-0.154117,-0.149527,-0.14493,-0.140325, + -0.135712,-0.131094,-0.12647,-0.121839,-0.117201,-0.112559,-0.10791, + -0.103257,-0.0985979,-0.0939343,-0.0892662,-0.0845935,-0.079917,-0.0752362, + -0.0705516,-0.0658635,-0.0611729,-0.0564786,-0.0517814,-0.0470818,-0.0423802, + -0.0376765,-0.0329703,-0.0282629,-0.0235542,-0.0188445,-0.0141335,-0.00942183, + -0.00470983,2.41979e-06,0.00471481,0.00942681,0.0141384,0.0188494,0.023559, + 0.028268,0.0329754,0.0376813,0.0423851,0.0470868,0.0517863,0.0564836, + 0.0611777,0.0658683,0.0705566,0.0752412,0.0799218,0.0845982,0.0892712, + 0.0939393,0.0986028,0.103262,0.107915,0.112563,0.117206,0.121844,0.126475, + 0.131099,0.135717,0.14033,0.144935,0.149531,0.154122,0.158705,0.163281, + 0.167847,0.172405,0.176956,0.1815,0.18603,0.190553,0.195069,0.199576, + 0.204068,0.208552,0.213028,0.217495,0.221947,0.226389,0.230822,0.235245, + 0.239653,0.244049,0.248436,0.252811,0.257173,0.26152,0.265857,0.270181, + 0.274491,0.278788,0.283071,0.287341,0.291597,0.29584,0.300068,0.30428, + 0.308478,0.312664,0.316833,0.320984,0.325122,0.329246,0.333354,0.337442, + 0.341516,0.345576,0.34962,0.353642,0.357649,0.361642,0.36562,0.369572, + 0.373509,0.377432,0.38134,0.385221,0.389086,0.392936,0.39677,0.400579, + 0.404369,0.408143,0.4119,0.415634,0.419347,0.423044,0.426721,0.430377, + 0.434011,0.437627,0.441223,0.444798,0.448351,0.451885,0.455397,0.458887, + 0.462359,0.465807,0.469232,0.472637,0.476024,0.479386,0.482723,0.486039, + 0.489338,0.492613,0.49586,0.499086,0.502294,0.505481,0.508635,0.511769, + 0.514885,0.517982,0.521042,0.524083,0.527105,0.530107,0.533074,0.53602, + 0.538946,0.541851,0.544725,0.547574,0.550404,0.553211,0.555989,0.55874, + 0.561472,0.564179,0.566859,0.569514,0.572146,0.574753,0.577332,0.579889, + 0.582421,0.584926,0.587405,0.589863,0.592294,0.594696,0.597073,0.59943, + 0.60176,0.604059,0.606333,0.608588,0.610818,0.613012,0.615183,0.617335, + 0.619464,0.621553,0.623621,0.625669,0.627696,0.629681,0.631645,0.633588, + 0.63551,0.637393,0.639253,0.641093,0.642909,0.644691,0.646446,0.648181, + 0.649892,0.651572,0.653223,0.654854,0.656459,0.658035,0.659585,0.661112, + 0.662611,0.664083,0.665532,0.666955,0.66835,0.669718,0.671066,0.672386, + 0.673676,0.674941,0.676187,0.677407,0.678592,0.679755,0.680898,0.682019, + 0.683101,0.684162,0.685204,0.686227,0.687206,0.688166,0.689108,0.69003, + 0.690911,0.691771,0.692613,0.693435,0.694219,0.694981,0.695725,0.696447, + 0.697135,0.6978,0.698447,0.69907,0.699664,0.700234,0.700786,0.701312, + 0.70181,0.702288,0.702746,0.703177,0.703582,0.703969,0.704334,0.704671, + 0.704984,0.705282,0.705557,0.705802,0.706025,0.706233,0.706422,0.706576, + 0.706712,0.706832,0.706936,0.707002,0.707051,0.707086,0.707106,0.707086, + 0.707051,0.707003,0.706939,0.706838,0.706721,0.706592,0.706445,0.706265, + 0.70607,0.705861,0.705634,0.705378,0.705105,0.70482,0.704515,0.704184, + 0.703837,0.703478,0.703097,0.702694,0.702276,0.701846,0.701392,0.700917, + 0.700432,0.699932,0.699408,0.698866,0.698314,0.697749,0.697156,0.696549, + 0.695933,0.695305,0.694648,0.693979,0.693301,0.692613,0.691894,0.691165, + 0.690428,0.689683,0.688905,0.68812,0.687327,0.686525,0.685693,0.684854, + 0.684009,0.683152,0.68227,0.68138,0.680485,0.679577,0.678647,0.67771, + 0.676768,0.675811,0.674836,0.673855,0.672869,0.671867,0.670849,0.669827, + 0.668801,0.667757,0.6667,0.66564,0.664576,0.663493,0.6624,0.661305, + 0.660207,0.659088,0.657962,0.656834,0.655705,0.654553,0.653398,0.652241, + 0.651085,0.649903,0.648721,0.647539,0.646356,0.645149,0.643944,0.642739, + 0.641532,0.640304,0.639079,0.637855,0.636626,0.635381,0.634139,0.632899, + 0.631652,0.630392,0.629136,0.627883,0.626622,0.62535,0.624083,0.62282, + 0.621548,0.620268,0.618993,0.617724,0.616443,0.615158,0.613878,0.612605, + 0.61132,0.610032,0.608751,0.607477,0.606189,0.604903,0.603623,0.602351, + 0.601065,0.599782,0.598508,0.59724,0.595957,0.594682,0.593415,0.592154, + 0.59088,0.589615,0.588359,0.587106,0.585843,0.584591,0.583349,0.582108, + 0.580859,0.579623,0.578397,0.577172,0.575939,0.574721,0.573515,0.572307, + 0.571095,0.569897,0.568713,0.567525,0.566337,0.565161,0.564002,0.562837, + 0.561674,0.560525,0.559392,0.558252,0.557119,0.555998,0.554893,0.553782, + 0.552679,0.55159,0.550516,0.549434,0.548365,0.54731,0.546269,0.54522, + 0.544187,0.543168,0.542161,0.541148,0.540153,0.539173,0.538202,0.537226, + 0.536271,0.535332,0.5344,0.533464,0.532551,0.531654,0.530762,0.52987, + 0.528999,0.528147,0.527298,0.526451,0.525624,0.524819,0.524014,0.523214, + 0.522432,0.521675,0.520916,0.520166,0.519433,0.518724,0.518012,0.517312, + 0.51663,0.51597,0.515307,0.51466,0.51403,0.51342,0.512808,0.512213, + 0.511638,0.511079,0.510518,0.509979,0.509458,0.508951,0.508444,0.50796, + 0.507495,0.507042,0.506589,0.506162,0.505754,0.505356,0.504958,0.504587, + 0.504237,0.503895,0.503555,0.50324,0.502949,0.502662,0.502382,0.502123, + 0.501891,0.501661,0.50144,0.501239,0.501066,0.500893,0.500732,0.500591, + 0.500476,0.50036,0.500259,0.500179,0.500121,0.500063,0.500023,0.500003,0.500003}; + //This is a lookup table for converting midi to frequency double mtofarray[129]={0, 8.661957, 9.177024, 9.722718, 10.3, 10.913383, 11.562325, 12.25, 12.978271, 13.75, 14.567617, 15.433853, 16.351599, 17.323914, 18.354048, 19.445436, 20.601723, 21.826765, 23.124651, 24.5, 25.956543, 27.5, 29.135235, 30.867706, 32.703197, 34.647827, 36.708096, 38.890873, 41.203445, 43.65353, 46.249302, 49., 51.913086, 55., 58.27047, 61.735413, 65.406395, 69.295654, 73.416191, 77.781746, 82.406891, 87.30706, 92.498604, 97.998856, 103.826172, 110., 116.540939, 123.470825, 130.81279, 138.591309, 146.832382, 155.563492, 164.813782, 174.61412, 184.997208, 195.997711, 207.652344, 220., 233.081879, 246.94165, 261.62558, 277.182617,293.664764, 311.126984, 329.627563, 349.228241, 369.994415, 391.995422, 415.304688, 440., 466.163757, 493.883301, 523.25116, 554.365234, 587.329529, 622.253967, 659.255127, 698.456482, 739.988831, 783.990845, 830.609375, 880., 932.327515, 987.766602, 1046.502319, 1108.730469, 1174.659058, 1244.507935, 1318.510254, 1396.912964, 1479.977661, 1567.981689, 1661.21875, 1760., 1864.655029, 1975.533203, 2093.004639, 2217.460938, 2349.318115, 2489.015869, 2637.020508, 2793.825928, 2959.955322, 3135.963379, 3322.4375, 3520., 3729.31, 3951.066406, 4186.009277, 4434.921875, 4698.63623, 4978.031738, 5274.041016, 5587.651855, 5919.910645, 6271.926758, 6644.875, 7040., 7458.620117, 7902.132812, 8372.018555, 8869.84375, 9397.272461, 9956.063477, 10548.082031, 11175.303711, 11839.821289, 12543.853516, 13289.75}; @@ -189,7 +325,31 @@ double maxiOsc::saw(double frequency) { phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); -} +} + +double maxiOsc::sawn(double frequency) { + //Bandlimited sawtooth generator. Woohoo. + if ( phase >= 0.5 ) phase -= 1.0; + phase += (1./(maxiSettings::sampleRate/(frequency))); + double temp=(8820.22/frequency)*phase; + if (temp<-0.5) { + temp=-0.5; + } + if (temp>0.5) { + temp=0.5; + } + temp*=1000.0f; + temp+=500.0f; + double remainder = temp - floor(temp); + output = (double) ((1.0f-remainder) * transition[(long)temp] + remainder * transition[1+(long)temp]) - phase; + return(output); + +} + +double maxiOsc::rect(double frequency, double duty) { + + return (output); +} double maxiOsc::triangle(double frequency) { //This is a triangle wave. @@ -208,7 +368,7 @@ double maxiEnvelope::line(int numberofsegments,double segments[1000]) { //This is a basic multi-segment ramp generator that you can use for more or less anything. //However, it's not that intuitive. if (isPlaying==1) {//only make a sound once you've been triggered - period=2./(segments[valindex+1]*0.004); + period=4./(segments[valindex+1]*0.0044); nextval=segments[valindex+2]; currentval=segments[valindex]; if (currentval-amplitude > 0.0000001 && valindex < numberofsegments) { @@ -280,9 +440,9 @@ double maxiFilter::hipass(double input, double cutoff) { } //awesome. cuttof is freq in hz. res is between 1 and whatever. Watch out! double maxiFilter::lores(double input,double cutoff1, double resonance) { - cutoff=cutoff1*0.5; + cutoff=cutoff1; if (cutoff<10) cutoff=10; - if (cutoff>(maxiSettings::sampleRate*0.5)) cutoff=(maxiSettings::sampleRate*0.5); + if (cutoff>(maxiSettings::sampleRate)) cutoff=(maxiSettings::sampleRate); if (resonance<1.) resonance = 1.; z=cos(TWOPI*cutoff/maxiSettings::sampleRate); c=2-2*z; @@ -296,9 +456,9 @@ double maxiFilter::lores(double input,double cutoff1, double resonance) { //working hires filter double maxiFilter::hires(double input,double cutoff1, double resonance) { - cutoff=cutoff1*0.5; + cutoff=cutoff1; if (cutoff<10) cutoff=10; - if (cutoff>(maxiSettings::sampleRate*0.5)) cutoff=(maxiSettings::sampleRate*0.5); + if (cutoff>(maxiSettings::sampleRate)) cutoff=(maxiSettings::sampleRate); if (resonance<1.) resonance = 1.; z=cos(TWOPI*cutoff/maxiSettings::sampleRate); c=2-2*z; @@ -367,18 +527,21 @@ double *maxiMix::ambisonic(double input,double eight[8],double x,double y,double return(eight); } - +//This is the maxiSample load function. It just calls read. bool maxiSample::load(string fileName, int channel) { myPath = fileName; readChannel=channel; return read(); } +// This is for OGG loading bool maxiSample::loadOgg(string fileName, int channel) { #ifdef VORBIS bool result; readChannel=channel; int channelx; +// cout << fileName << endl; + free(temp); myDataSize = stb_vorbis_decode_filename(const_cast<char*>(fileName.c_str()), &channelx, &temp); result = myDataSize > 0; printf("\nchannels = %d\nlength = %d",channelx,myDataSize); @@ -400,16 +563,19 @@ bool maxiSample::loadOgg(string fileName, int channel) { return 0; } +//This sets the playback position to the start of a sample void maxiSample::trigger() { position = 0; + recordPosition = 0; } +//This is the main read function. bool maxiSample::read() { bool result; ifstream inFile( myPath.c_str(), ios::in | ios::binary); - result = inFile; - if (inFile) { + result = inFile.is_open(); + if (result) { bool datafound = false; inFile.seekg(4, ios::beg); inFile.read( (char*) &myChunkSize, 4 ); // read the ChunkSize @@ -438,7 +604,7 @@ bool maxiSample::read() //ignore any extra chunks char chunkID[5]=""; chunkID[4] = 0; - int filePos = 36; + int filePos = 20 + mySubChunk1Size; while(!datafound && !inFile.eof()) { inFile.seekg(filePos, ios::beg); inFile.read((char*) &chunkID, sizeof(char) * 4); @@ -453,7 +619,7 @@ bool maxiSample::read() } // read the data chunk - char *myData = (char*) malloc(myDataSize * sizeof(char)); + char * myData = (char*) malloc(myDataSize * sizeof(char)); inFile.seekg(filePos, ios::beg); inFile.read(myData, myDataSize); length=myDataSize*(0.5/myChannels); @@ -469,6 +635,7 @@ bool maxiSample::read() position+=2; } } + free(temp); temp = (short*) malloc(myDataSize * sizeof(char)); memcpy(temp, myData, myDataSize * sizeof(char)); @@ -484,13 +651,39 @@ bool maxiSample::read() return result; // this should probably be something more descriptive } +//This plays back at the correct speed. Always loops. double maxiSample::play() { position++; - if ((long) position == length) position=0; + if ((long) position >= length) position=0; output = (double) temp[(long)position]/32767.0; return output; } +void maxiSample::setPosition(double newPos) { + position = maxiMap::clamp<double>(newPos, 0.0, 1.0) * length; +} + +//start end and points are between 0 and 1 +double maxiSample::playLoop(double start, double end) { + position++; + if (position < length * start) position = length * start; + if ((long) position >= length * end) position = length * start; + output = (double) temp[(long)position]/32767.0; + return output; +} + +double maxiSample::playUntil(double end) { + position++; + if ((long) position<length * end) + output = (double) temp[(long)position]/32767.0; + else { + output=0; + } + return output; +} + + +//This plays back at the correct speed. Only plays once. To retrigger, you have to manually reset the position double maxiSample::playOnce() { position++; if ((long) position<length) @@ -502,6 +695,7 @@ double maxiSample::playOnce() { } +//Same as above but takes a speed value specified as a ratio, with 1.0 as original speed double maxiSample::playOnce(double speed) { position=position+((speed*chandiv)/(maxiSettings::sampleRate/mySampleRate)); double remainder = position - (long) position; @@ -512,6 +706,7 @@ double maxiSample::playOnce(double speed) { return(output); } +//As above but looping double maxiSample::play(double speed) { double remainder; long a,b; @@ -557,10 +752,12 @@ double maxiSample::play(double speed) { return(output); } +//placeholder double maxiSample::play(double frequency, double start, double end) { return play(frequency, start, end, position); } +//This allows you to say how often a second you want a specific chunk of audio to play double maxiSample::play(double frequency, double start, double end, double &pos) { double remainder; if (end>=length) end=length-1; @@ -572,7 +769,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) } if ( pos >= end ) pos = start; - pos += ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); + pos += ((end-start)/((maxiSettings::sampleRate)/(frequency*chandiv))); remainder = pos - floor(pos); long posl = floor(pos); if (posl+1<length) { @@ -592,7 +789,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) output = (double) ((1-remainder) * temp[a] + remainder * temp[b])/32767;//linear interpolation } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( pos <= start ) pos = end; pos -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = pos - floor(pos); @@ -618,7 +815,7 @@ double maxiSample::play(double frequency, double start, double end, double &pos) } -//better cubic inerpolation. Cobbled together from various (pd externals, yehar, other places). +//Same as above. better cubic inerpolation. Cobbled together from various (pd externals, yehar, other places). double maxiSample::play4(double frequency, double start, double end) { double remainder; double a,b,c,d,a1,a2,a3; @@ -657,7 +854,7 @@ double maxiSample::play4(double frequency, double start, double end) { output = (double) (((a3 * remainder + a2) * remainder + a1) * remainder + b) / 32767; } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -693,6 +890,8 @@ double maxiSample::play4(double frequency, double start, double end) { return(output); } + +//You don't need to worry about this stuff. double maxiSample::bufferPlay(unsigned char &bufferin,long length) { double remainder; short* buffer = (short *)&bufferin; @@ -780,7 +979,7 @@ double maxiSample::bufferPlay(unsigned char &bufferin,double frequency, double s output = (double) ((1-remainder) * buffer[a] + remainder * buffer[b])/32767;//linear interpolation } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -845,7 +1044,7 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double output = (double) (((a3 * remainder + a2) * remainder + a1) * remainder + b) / 32767; } else { - frequency=frequency-(frequency+frequency); + frequency*=-1.; if ( position <= start ) position = end; position -= ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); @@ -883,18 +1082,17 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double void maxiSample::getLength() { - length=myDataSize*0.5; + length=myDataSize*0.5; } void maxiSample::setLength(unsigned long numSamples) { - temp = (short*) realloc(temp, sizeof(short) * numSamples); -// short *newData = (short*) malloc(sizeof(short) * numSamples); -// if (NULL!=temp) { -// unsigned long copyLength = min((unsigned long)length, numSamples); -// memcpy(newData, temp, sizeof(short) * copyLength); -// free(temp); -// } -// temp = newData; + cout << "Length: " << numSamples << endl; + short *newData = (short*) malloc(sizeof(short) * numSamples); + if (NULL!=temp) { + unsigned long copyLength = min((unsigned long)length, numSamples); + memcpy(newData, temp, sizeof(short) * copyLength); + } + temp = newData; myDataSize = numSamples * 2; length=numSamples; position=0; @@ -909,6 +1107,69 @@ void maxiSample::reset() { position=0; } +void maxiSample::normalise(float maxLevel) { + short maxValue = 0; + for(int i=0; i < length; i++) { + if (abs(temp[i]) > maxValue) { + maxValue = abs(temp[i]); + } + } + float scale = 32767.0 * maxLevel / (float) maxValue; + for(int i=0; i < length; i++) { + temp[i] = round(scale * (float) temp[i]); + } +} + +void maxiSample::autoTrim(float alpha, float threshold, bool trimStart, bool trimEnd) { + + int startMarker=0; + if(trimStart) { + maxiLagExp<float> startLag(alpha, 0); + while(startMarker < length) { + startLag.addSample(abs(temp[startMarker])); + if (startLag.value() > threshold) { + break; + } + startMarker++; + } + } + + int endMarker = length-1; + if(trimEnd) { + maxiLagExp<float> endLag(alpha, 0); + while(endMarker > 0) { + endLag.addSample(abs(temp[endMarker])); + if (endLag.value() > threshold) { + break; + } + endMarker--; + } + } + + cout << "Autotrim: start: " << startMarker << ", end: " << endMarker << endl; + + int newLength = endMarker - startMarker; + if (newLength > 0) { + short *newData = (short*) malloc(sizeof(short) * newLength); + for(int i=0; i < newLength; i++) { + newData[i] = temp[i+startMarker]; + } + free(temp); + temp = newData; + myDataSize = newLength * 2; + length=newLength; + position=0; + recordPosition=0; + //envelope the start + int fadeSize=min((long)100, length); + for(int i=0; i < fadeSize; i++) { + float factor = i / (float) fadeSize; + temp[i] = round(temp[i] * factor); + temp[length - 1 - i] = round(temp[length - 1 - i] * factor); + } + } +} + @@ -986,6 +1247,37 @@ double maxiDyn::compressor(double input, double ratio, double threshold, double return output*(1+log(ratio)); } +double maxiDyn::compress(double input) { + + if (fabs(input)>threshold && attackphase!=1){ + holdcount=0; + releasephase=0; + attackphase=1; + if(currentRatio==0) currentRatio=ratio; + } + + if (attackphase==1 && currentRatio<ratio-1) { + currentRatio*=(1+attack); + } + + if (currentRatio>=ratio-1) { + attackphase=0; + releasephase=1; + } + + if (releasephase==1 && currentRatio>0.) { + currentRatio*=release; + } + + if (input>0.) { + output = input/(1.+currentRatio); + } else { + output = input/(1.+currentRatio); + } + + return output*(1+log(ratio)); +} + /* Lots of people struggle with the envelope generators so here's a new easy one. It takes mental numbers for attack and release tho. Basically, they're exponentials. @@ -1031,70 +1323,159 @@ double maxiEnv::ar(double input, double attack, double release, long holdtime, i return output; } -/* and here's a new adsr. It's not bad, very simple to use*/ +/* adsr. It's not bad, very simple to use*/ double maxiEnv::adsr(double input, double attack, double decay, double sustain, double release, long holdtime, int trigger) { - - if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ - holdcount=0; - decayphase=0; - sustainphase=0; - releasephase=0; - attackphase=1; - } - - if (attackphase==1) { - amplitude+=(1*attack); - output=input*amplitude; - } - - if (amplitude>=1) { - amplitude=1; - attackphase=0; - decayphase=1; - } - - if (decayphase==1) { - output=input*(amplitude*=decay); - if (amplitude<=sustain) { - decayphase=0; - holdphase=1; - } - } - - if (holdcount<holdtime && holdphase==1) { - output=input*amplitude; - holdcount++; - } - - if (holdcount==holdtime && trigger==1) { - output=input*amplitude; - } - - if (holdcount==holdtime && trigger!=1) { - holdphase=0; - releasephase=1; - } - - if (releasephase==1 && amplitude>0.) { - output=input*(amplitude*=release); - - } - - return output; + + if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ + holdcount=0; + decayphase=0; + sustainphase=0; + releasephase=0; + attackphase=1; + } + + if (attackphase==1) { + releasephase=0; + amplitude+=(1*attack); + output=input*amplitude; + + if (amplitude>=1) { + amplitude=1; + attackphase=0; + decayphase=1; + } + } + + + if (decayphase==1) { + output=input*(amplitude*=decay); + if (amplitude<=sustain) { + decayphase=0; + holdphase=1; + } + } + + if (holdcount<holdtime && holdphase==1) { + output=input*amplitude; + holdcount++; + } + + if (holdcount>=holdtime && trigger==1) { + output=input*amplitude; + } + + if (holdcount>=holdtime && trigger!=1) { + holdphase=0; + releasephase=1; + } + + if (releasephase==1 && amplitude>0.) { + output=input*(amplitude*=release); + + } + + return output; } +double maxiEnv::adsr(double input, int trigger) { + + if (trigger==1 && attackphase!=1 && holdphase!=1 && decayphase!=1){ + holdcount=0; + decayphase=0; + sustainphase=0; + releasephase=0; + attackphase=1; + } + + if (attackphase==1) { + releasephase=0; + amplitude+=(1*attack); + output=input*amplitude; + + if (amplitude>=1) { + amplitude=1; + attackphase=0; + decayphase=1; + } + } + + + if (decayphase==1) { + output=input*(amplitude*=decay); + if (amplitude<=sustain) { + decayphase=0; + holdphase=1; + } + } + + if (holdcount<holdtime && holdphase==1) { + output=input*amplitude; + holdcount++; + } + + if (holdcount>=holdtime && trigger==1) { + output=input*amplitude; + } + + if (holdcount>=holdtime && trigger!=1) { + holdphase=0; + releasephase=1; + } + + if (releasephase==1 && amplitude>0.) { + output=input*(amplitude*=release); + + } + + return output; +} + + +void maxiEnv::setAttack(double attackMS) { + attack = 1-pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiEnv::setRelease(double releaseMS) { + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiEnv::setSustain(double sustainL) { + sustain = sustainL; +} + +void maxiEnv::setDecay(double decayMS) { + decay = pow( 0.01, 1.0 / ( decayMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setAttack(double attackMS) { + attack = pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setRelease(double releaseMS) { + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); +} + +void maxiDyn::setThreshold(double thresholdI) { + threshold = thresholdI; +} + +void maxiDyn::setRatio(double ratioF) { + ratio = ratioF; +} + + double convert::mtof(int midinote) { return mtofarray[midinote]; } -void maxiEnvelopeFollower::setAttack(double attackMS) { +template<> void maxiEnvelopeFollower::setAttack(double attackMS) { attack = pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); } -void maxiEnvelopeFollower::setRelease(double releaseMS) { +template<> void maxiEnvelopeFollower::setRelease(double releaseMS) { release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); } diff --git a/ofxMaxim/openFrameworksExamples/windows/ofxMaximExampleVS2010-OF0072/src/ofxMaxim/libs/maximilian.h b/ofxMaxim/openFrameworksExamples/windows/ofxMaximExampleVS2010-OF0072/src/ofxMaxim/libs/maximilian.h index dd942b7..8a78c26 100644..100755 --- a/ofxMaxim/openFrameworksExamples/windows/ofxMaximExampleVS2010-OF0072/src/ofxMaxim/libs/maximilian.h +++ b/ofxMaxim/openFrameworksExamples/windows/ofxMaximExampleVS2010-OF0072/src/ofxMaxim/libs/maximilian.h @@ -43,6 +43,10 @@ #include <cstdlib> #include "math.h" +#ifdef _WIN32 //|| _WIN64 +#include <algorithm> +#endif + using namespace std; #ifndef PI #define PI 3.1415926535897932384626433832795 @@ -85,6 +89,8 @@ public: double noise(); double sinebuf(double frequency); double sinebuf4(double frequency); + double sawn(double frequency); + double rect(double frequency, double duty=0.5); void phaseReset(double phaseIn); }; @@ -207,12 +213,12 @@ private: int myByteRate; short myBlockAlign; short myBitsPerSample; + double position, recordPosition; double speed; double output; maxiLagExp<double> loopRecordLag; public: - double position, recordPosition; int myDataSize; short myChannels; int mySampleRate; @@ -230,9 +236,11 @@ public: { // if (myData) free(myData); if (temp) free(temp); + printf("freeing SampleData"); + } - maxiSample():temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; + maxiSample():temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; maxiSample& operator=(const maxiSample &source) { if (this == &source) @@ -261,8 +269,9 @@ public: //read an ogg file into this class using stb_vorbis bool readOgg(); - void loopRecord(double newSample, const bool recordEnabled, const double recordMix) { + void loopRecord(double newSample, const bool recordEnabled, const double recordMix, double start = 0.0, double end = 1.0) { loopRecordLag.addSample(recordEnabled); + if (recordPosition < start * length) recordPosition = start * length; if(recordEnabled) { double currentSample = temp[(unsigned long)recordPosition] / 32767.0; newSample = (recordMix * currentSample) + ((1.0 - recordMix) * newSample); @@ -270,61 +279,67 @@ public: temp[(unsigned long)recordPosition] = newSample * 32767; } ++recordPosition; - if (recordPosition == length) - recordPosition=0; + if (recordPosition >= end * length) + recordPosition= start * length; } void clear(); void reset(); - - double play(); - - double playOnce(); - - double playOnce(double speed); - - double play(double speed); - - double play(double frequency, double start, double end, double &pos); - - double play(double frequency, double start, double end); - - double play4(double frequency, double start, double end); - - double bufferPlay(unsigned char &bufferin,long length); - - double bufferPlay(unsigned char &bufferin,double speed,long length); - - double bufferPlay(unsigned char &bufferin,double frequency, double start, double end); - - double bufferPlay4(unsigned char &bufferin,double frequency, double start, double end); + + double play(); + + double playLoop(double start, double end); // start and end are between 0.0 and 1.0 + + double playOnce(); + + double playOnce(double speed); + + void setPosition(double newPos); // between 0.0 and 1.0 + + double playUntil(double end); + + double play(double speed); + + double play(double frequency, double start, double end, double &pos); + + double play(double frequency, double start, double end); + + double play4(double frequency, double start, double end); + + double bufferPlay(unsigned char &bufferin,long length); + + double bufferPlay(unsigned char &bufferin,double speed,long length); + + double bufferPlay(unsigned char &bufferin,double frequency, double start, double end); + + double bufferPlay4(unsigned char &bufferin,double frequency, double start, double end); bool save() { return save(myPath); } bool save(string filename) { - fstream myFile (filename.c_str(), ios::out | ios::binary); - - // write the wav file per the wav file format - myFile.seekp (0, ios::beg); - myFile.write ("RIFF", 4); - myFile.write ((char*) &myChunkSize, 4); - myFile.write ("WAVE", 4); - myFile.write ("fmt ", 4); - myFile.write ((char*) &mySubChunk1Size, 4); - myFile.write ((char*) &myFormat, 2); - myFile.write ((char*) &myChannels, 2); - myFile.write ((char*) &mySampleRate, 4); - myFile.write ((char*) &myByteRate, 4); - myFile.write ((char*) &myBlockAlign, 2); - myFile.write ((char*) &myBitsPerSample, 2); - myFile.write ("data", 4); - myFile.write ((char*) &myDataSize, 4); - myFile.write ((char*) temp, myDataSize); - - return true; + fstream myFile (filename.c_str(), ios::out | ios::binary); + + // write the wav file per the wav file format + myFile.seekp (0, ios::beg); + myFile.write ("RIFF", 4); + myFile.write ((char*) &myChunkSize, 4); + myFile.write ("WAVE", 4); + myFile.write ("fmt ", 4); + myFile.write ((char*) &mySubChunk1Size, 4); + myFile.write ((char*) &myFormat, 2); + myFile.write ((char*) &myChannels, 2); + myFile.write ((char*) &mySampleRate, 4); + myFile.write ((char*) &myByteRate, 4); + myFile.write ((char*) &myBlockAlign, 2); + myFile.write ((char*) &myBitsPerSample, 2); + myFile.write ("data", 4); + myFile.write ((char*) &myDataSize, 4); + myFile.write ((char*) temp, myDataSize); + + return true; } // return a printable summary of the wav file @@ -332,49 +347,58 @@ public: { char *summary = new char[250]; sprintf(summary, " Format: %d\n Channels: %d\n SampleRate: %d\n ByteRate: %d\n BlockAlign: %d\n BitsPerSample: %d\n DataSize: %d\n", myFormat, myChannels, mySampleRate, myByteRate, myBlockAlign, myBitsPerSample, myDataSize); + std::cout << myDataSize; return summary; } + + void normalise(float maxLevel = 0.99); //0 < maxLevel < 1.0 + void autoTrim(float alpha = 0.3, float threshold = 6000, bool trimStart = true, bool trimEnd = true); //alpha of lag filter (lower == slower reaction), threshold to mark start and end, < 32767 }; class maxiMap { public: - static double inline linlin(double val, double inMin, double inMax, double outMin, double outMax) { - return ((val - inMin) / (inMax - inMin) * (outMax - outMin)) + outMin; - } - - static double inline linexp(double val, double inMin, double inMax, double outMin, double outMax) { - //clipping - val = max(min(val, inMax), inMin); - return pow((outMax / outMin), (val - inMin) / (inMax - inMin)) * outMin; - } - - static double inline explin(double val, double inMin, double inMax, double outMin, double outMax) { - //clipping - val = max(min(val, inMax), inMin); - return (log(val/inMin) / log(inMax/inMin) * (outMax - outMin)) + outMin; - } - + static double inline linlin(double val, double inMin, double inMax, double outMin, double outMax) { + val = max(min(val, inMax), inMin); + return ((val - inMin) / (inMax - inMin) * (outMax - outMin)) + outMin; + } + + static double inline linexp(double val, double inMin, double inMax, double outMin, double outMax) { + //clipping + val = max(min(val, inMax), inMin); + return pow((outMax / outMin), (val - inMin) / (inMax - inMin)) * outMin; + } + + static double inline explin(double val, double inMin, double inMax, double outMin, double outMax) { + //clipping + val = max(min(val, inMax), inMin); + return (log(val/inMin) / log(inMax/inMin) * (outMax - outMin)) + outMin; + } + //changed to templated function, e.g. maxiMap::maxiClamp<int>(v, l, h); template<typename T> - static T inline clamp(T v, const T low, const T high) { + static T inline clamp(T v, const T low, const T high) { if (v > high) v = high; else if (v < low) { v = low; } - return v; - } + return v; + } }; + class maxiDyn { public: - double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); - double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); - double input; +// double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); +// double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); + double gate(double input, double threshold=0.9, long holdtime=1, double attack=1, double release=0.9995); + double compressor(double input, double ratio, double threshold=0.9, double attack=1, double release=0.9995); + double compress(double input); + double input; double ratio; double currentRatio; double threshold; @@ -382,6 +406,10 @@ public: double attack; double release; double amplitude; + void setAttack(double attackMS); + void setRelease(double releaseMS); + void setThreshold(double thresholdI); + void setRatio(double ratioF); long holdtime; long holdcount; int attackphase,holdphase,releasephase; @@ -393,6 +421,7 @@ class maxiEnv { public: double ar(double input, double attack=1, double release=0.9, long holdtime=1, int trigger=0); double adsr(double input, double attack=1, double decay=0.99, double sustain=0.125, double release=0.9, long holdtime=1, int trigger=0); + double adsr(double input,int trigger); double input; double output; double attack; @@ -400,8 +429,12 @@ public: double sustain; double release; double amplitude; + void setAttack(double attackMS); + void setRelease(double releaseMS); + void setDecay(double decayMS); + void setSustain(double sustainL); int trigger; - long holdtime; + long holdtime=1; long holdcount; int attackphase,decayphase,sustainphase,holdphase,releasephase; }; @@ -489,30 +522,38 @@ inline double maxiChorus::chorus(const double input, const unsigned int delay, c return (output1 + output2 + input) / 3.0; } -class maxiEnvelopeFollower { +template<typename T> +class maxiEnvelopeFollowerType { public: - maxiEnvelopeFollower() { + maxiEnvelopeFollowerType() { setAttack(100); setRelease(100); env = 0; } - void setAttack(double attackMS); - void setRelease(double releaseMS); - inline double play(double input) { + void setAttack(T attackMS) { + attack = pow( 0.01, 1.0 / (attackMS * maxiSettings::sampleRate * 0.001 ) ); + } + void setRelease(T releaseMS) { + release = pow( 0.01, 1.0 / (releaseMS * maxiSettings::sampleRate * 0.001 ) ); + } + inline T play(T input) { input = fabs(input); if (input>env) env = attack * (env - input) + input; else - env = release * (env - input) + input; + env = release * (env - input) + input; return env; } - void reset() {env=0;} - inline double getEnv(){return env;} + void reset() {env=0;} + inline T getEnv(){return env;} + inline void setEnv(T val){env = val;} private: - double attack, release, env; + T attack, release, env; }; -//from https://ccrma.stanford.edu/~jos/filters/DC_Blocker_Software_Implementations.html +typedef maxiEnvelopeFollowerType<double> maxiEnvelopeFollower; +typedef maxiEnvelopeFollowerType<float> maxiEnvelopeFollowerF; + class maxiDCBlocker { public: double xm1, ym1; @@ -524,5 +565,73 @@ public: } }; +/* + State Variable Filter + + algorithm from http://www.cytomic.com/files/dsp/SvfLinearTrapOptimised.pdf + usage: + either set the parameters separately as required (to save CPU) + + filter.setCutoff(param1); + filter.setResonance(param2); + + w = filter.play(w, 0.0, 1.0, 0.0, 0.0); + + or set everything together at once + + w = filter.setCutoff(param1).setResonance(param2).play(w, 0.0, 1.0, 0.0, 0.0); + + */ +class maxiSVF { +public: + maxiSVF() : v0z(0), v1(0), v2(0) { setParams(1000, 1);} + + //20 < cutoff < 20000 + inline maxiSVF& setCutoff(double cutoff) { + setParams(cutoff, res); + return *this; + } + + //from 0 upwards, starts to ring from 2-3ish, cracks a bit around 10 + inline maxiSVF& setResonance(double q) { + setParams(freq, q); + return *this; + } + + //run the filter, and get a mixture of lowpass, bandpass, highpass and notch outputs + inline double play(double w, double lpmix, double bpmix, double hpmix, double notchmix) { + double low, band, high, notch; + double v1z = v1; + double v2z = v2; + double v3 = w + v0z - 2.0 * v2z; + v1 += g1*v3-g2*v1z; + v2 += g3*v3+g4*v1z; + v0z = w; + low = v2; + band = v1; + high = w-k*v1-v2; + notch = w-k*v1; + return (low * lpmix) + (band * bpmix) + (high * hpmix) + (notch * notchmix); + } + +private: + inline void setParams(double _freq, double _res) { + freq = _freq; + res = _res; + g = tan(PI * freq / maxiSettings::sampleRate); + damping = res == 0 ? 0 : 1.0 / res; + k = damping; + ginv = g / (1.0 + g * (g + k)); + g1 = ginv; + g2 = 2.0 * (g + k) * ginv; + g3 = g * ginv; + g4 = 2.0 * ginv; + } + + double v0z, v1, v2, g, damping, k, ginv, g1, g2, g3 ,g4; + double freq, res; + +}; + #endif |
