diff --git a/maximilian.cpp b/maximilian.cpp index 6ca26bdd..6d6bffe3 100644 --- a/maximilian.cpp +++ b/maximilian.cpp @@ -16,11 +16,11 @@ * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: - * + * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT @@ -34,7 +34,7 @@ #include "maximilian.h" #include "math.h" -/* Maximilian can be configured to load ogg vorbis format files using the +/* Maximilian can be configured to load ogg vorbis format files using the * loadOgg() method. * 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*/ @@ -55,7 +55,7 @@ int maxiSettings::channels = 2; int maxiSettings::bufferSize = 1024; -//this is a 514-point sinewave table that has many uses. +//this is a 514-point sinewave table that has many uses. 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 }; @@ -200,7 +200,7 @@ double mtofarray[129]={0, 8.661957, 9.177024, 9.722718, 10.3, 10.913383, 11.5623 void setup();//use this to do any initialisation if you want. -void play(double *channels);//run dac! +void play(double *channels);//run dac! maxiOsc::maxiOsc(){ //When you create an oscillator, the constructor sets the phase of the oscillator to 0. @@ -218,7 +218,7 @@ double maxiOsc::noise() { void maxiOsc::phaseReset(double phaseIn) { //This allows you to set the phase of the oscillator to anything you like. phase=phaseIn; - + } double maxiOsc::sinewave(double frequency) { @@ -227,7 +227,7 @@ double maxiOsc::sinewave(double frequency) { if ( phase >= 1.0 ) phase -= 1.0; phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); - + } double maxiOsc::sinebuf4(double frequency) { @@ -237,21 +237,21 @@ double maxiOsc::sinebuf4(double frequency) { phase += 512./(maxiSettings::sampleRate/(frequency)); if ( phase >= 511 ) phase -=512; remainder = phase - floor(phase); - + if (phase==0) { a=sineBuffer[(long) 512]; b=sineBuffer[(long) phase]; c=sineBuffer[(long) phase+1]; d=sineBuffer[(long) phase+2]; - + } else { a=sineBuffer[(long) phase-1]; b=sineBuffer[(long) phase]; c=sineBuffer[(long) phase+1]; d=sineBuffer[(long) phase+2]; - + } - + a1 = 0.5f * (c - a); a2 = a - 2.5 * b + 2.f * c - 0.5f * d; a3 = 0.5f * (d - a) + 1.5f * (b - c); @@ -275,16 +275,16 @@ double maxiOsc::coswave(double frequency) { if ( phase >= 1.0 ) phase -= 1.0; phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); - + } double maxiOsc::phasor(double frequency) { - //This produces a floating point linear ramp between 0 and 1 at the desired frequency + //This produces a floating point linear ramp between 0 and 1 at the desired frequency output=phase; if ( phase >= 1.0 ) phase -= 1.0; phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); -} +} double maxiOsc::square(double frequency) { //This is a square wave @@ -307,7 +307,7 @@ double maxiOsc::pulse(double frequency, double duty) { } double maxiOsc::phasor(double frequency, double startphase, double endphase) { - //This is a phasor that takes a value for the start and end of the ramp. + //This is a phasor that takes a value for the start and end of the ramp. output=phase; if (phase= 1.0 ) phase -= 2.0; phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); - + } double maxiOsc::sawn(double frequency) { @@ -343,7 +343,7 @@ double maxiOsc::sawn(double frequency) { 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) { @@ -361,8 +361,8 @@ double maxiOsc::triangle(double frequency) { 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. @@ -382,11 +382,11 @@ double maxiEnvelope::line(int numberofsegments,double segments[1000]) { startval=currentval; } output=amplitude; - + } else { output=0; - + } return(output); } @@ -396,12 +396,12 @@ void maxiEnvelope::trigger(int index, double amp) { isPlaying=1;//ok the envelope is being used now. valindex=index; amplitude=amp; - + } //Delay with feedback maxiDelayline::maxiDelayline() { - memset( memory, 0, 88200*sizeof (double) ); + memset( memory, 0, 88200*sizeof (double) ); } @@ -413,7 +413,7 @@ double maxiDelayline::dl(double input, int size, double feedback) { memory[phase]=(memory[phase]*feedback)+(input*feedback)*0.5; phase+=1; return(output); - + } double maxiDelayline::dl(double input, int size, double feedback, int position) { @@ -423,7 +423,7 @@ double maxiDelayline::dl(double input, int size, double feedback, int position) memory[phase]=(memory[phase]*feedback)+(input*feedback)*chandiv; phase+=1; return(output); - + } //I particularly like these. cutoff between 0 and 1 @@ -479,13 +479,80 @@ double maxiFilter::bandpass(double input,double cutoff1, double resonance) { inputs[0] = (1-resonance)*(sqrt(resonance*(resonance-4.0*pow(z,2.0)+2.0)+1)); inputs[1] = 2*z*resonance; inputs[2] = pow((resonance*-1),2); - + output=inputs[0]*input+inputs[1]*outputs[1]+inputs[2]*outputs[2]; outputs[2]=outputs[1]; outputs[1]=output; return(output); } +//Peaking filter (emphasizes a certain frequency band) +//based on the cookbook by Robert Bristow-Johnson +//http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +//implemented by Jakub Fiala +//q ranges from 0 to 1 +//so does gain + +double maxiFilter::peaking(double input, double centerFreq, double q, double gain) { + cutoff = centerFreq; + gain *= 4; + //calculate poles and coeffs + w0 = TWOPI * cutoff / 44100; + alpha = sin(w0)/(2*q); + b0 = 1 + alpha*gain; + b1 = -2*cos(w0); + b2 = 1 - alpha*gain; + a0 = 1 + alpha/gain; + a1 = -2*cos(w0); + a2 = 1 - alpha/gain; + + //apply filter + output = (b0/a0)*input + (b1/a0)*in1 + (b2/a0)*in2 - (a1/a0)*out1 - (a2/a0)*out2; + + //update memory + out2 = out1; + out1 = output; + in2 = in1; + in1 = input; + + return output; +} + +//Formant filter (basically a resonant bandpass) +//based on the cookbook by Robert Bristow-Johnson +//http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +//implemented by Jakub Fiala +//q ranges from 0 to 1 +//so does gain + +double maxiFilter::formant(double input, double centerFreq, double q, double gain) { + cutoff = centerFreq; + + //offset gain + gain += 1; + //gain *= 4; + //calculate poles and coeffs + w0 = TWOPI * cutoff / 44100; + alpha = sin(w0)/(20*q); + b0 = alpha*gain; + b1 = 0; + b2 = -alpha*gain; + a0 = 1 + alpha/gain; + a1 = -2*cos(w0); + a2 = 1 - alpha/gain; + + //apply filter + output = (b0/a0)*input + (b1/a0)*in1 + (b2/a0)*in2 - (a1/a0)*out1 - (a2/a0)*out2; + + //update memory + out2 = out1; + out1 = output; + in2 = in1; + in1 = input; + + return output; +} + //stereo bus double *maxiMix::stereo(double input,double two[2],double x) { if (x>1) x=1; @@ -493,7 +560,7 @@ double *maxiMix::stereo(double input,double two[2],double x) { two[0]=input*sqrt(1.0-x); two[1]=input*sqrt(x); return(two); -} +} //quad bus double *maxiMix::quad(double input,double four[4],double x,double y) { @@ -548,7 +615,7 @@ bool maxiSample::loadOgg(string fileName, int channel) { myChannels=(short)channelx; length=myDataSize; mySampleRate=44100; - + if (myChannels>1) { int position=0; int channel=readChannel; @@ -577,28 +644,28 @@ bool maxiSample::read() bool datafound = false; inFile.seekg(4, ios::beg); inFile.read( (char*) &myChunkSize, 4 ); // read the ChunkSize - + inFile.seekg(16, ios::beg); inFile.read( (char*) &mySubChunk1Size, 4 ); // read the SubChunk1Size - + //inFile.seekg(20, ios::beg); inFile.read( (char*) &myFormat, sizeof(short) ); // read the file format. This should be 1 for PCM - + //inFile.seekg(22, ios::beg); inFile.read( (char*) &myChannels, sizeof(short) ); // read the # of channels (1 or 2) - + //inFile.seekg(24, ios::beg); inFile.read( (char*) &mySampleRate, sizeof(int) ); // read the samplerate - + //inFile.seekg(28, ios::beg); inFile.read( (char*) &myByteRate, sizeof(int) ); // read the byterate - + //inFile.seekg(32, ios::beg); inFile.read( (char*) &myBlockAlign, sizeof(short) ); // read the blockalign - + //inFile.seekg(34, ios::beg); inFile.read( (char*) &myBitsPerSample, sizeof(short) ); // read the bitspersample - + //ignore any extra chunks char chunkID[5]=""; chunkID[4] = 0; @@ -615,14 +682,14 @@ bool maxiSample::read() filePos += myDataSize; } } - + // read the data chunk myData = (char*) malloc(myDataSize * sizeof(char)); inFile.seekg(filePos, ios::beg); inFile.read(myData, myDataSize); length=myDataSize*(0.5/myChannels); inFile.close(); // close the input file - + cout << "Ch: " << myChannels << ", len: " << length << endl; if (myChannels>1) { int position=0; @@ -635,16 +702,16 @@ bool maxiSample::read() } temp = (short*) malloc(myDataSize * sizeof(char)); memcpy(temp, myData, myDataSize * sizeof(char)); - + free(myData); - + }else { -// cout << "ERROR: Could not load sample: " <=0) { - + if ((long) position>=length-1) position=1; remainder = position - floor(position); if (position+1=0) { a=position-1; - + } else { a=0; @@ -721,7 +788,7 @@ double maxiSample::play(double speed) { b=0; } output = (double) ((-1-remainder) * temp[a] + remainder * temp[b])/32767;//linear interpolation - } + } return(output); } @@ -740,14 +807,14 @@ double maxiSample::play(double frequency, double start, double end, double &pos) if (pos= end ) pos = start; pos += ((end-start)/((maxiSettings::sampleRate)/(frequency*chandiv))); remainder = pos - floor(pos); long posl = floor(pos); if (posl+1start && position < end-1) { a=temp[(long) position+1]; - + } else { a=temp[0]; - + } - + b=temp[(long) position]; if (position>start) { c=temp[(long) position-1]; - + } else { c=temp[0]; - + } if (position>start+1) { d=temp[(long) position-2]; - + } else { d=temp[0]; } @@ -857,9 +924,9 @@ double maxiSample::play4(double frequency, double start, double end) { a2 = a - 2.5 * b + 2.f * c - 0.5f * d; a3 = 0.5f * (d - a) + 1.5f * (b - c); output = (double) (((a3 * remainder + a2) * -remainder + a1) * -remainder + b) / 32767; - + } - + return(output); } @@ -881,12 +948,12 @@ double maxiSample::bufferPlay(unsigned char &bufferin,double speed,long length) short* buffer = (short *)&bufferin; position=position+((speed*chandiv)/(maxiSettings::sampleRate/mySampleRate)); if (speed >=0) { - + if ((long) position>=length-1) position=1; remainder = position - floor(position); if (position+1=0) { a=position-1; - + } else { a=0; @@ -917,7 +984,7 @@ double maxiSample::bufferPlay(unsigned char &bufferin,double speed,long length) b=0; } output = (double) ((-1-remainder) * buffer[a] + remainder * buffer[b])/32767;//linear interpolation - } + } return(output); } @@ -930,14 +997,14 @@ double maxiSample::bufferPlay(unsigned char &bufferin,double frequency, double s if (position= end ) position = start; position += ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); long pos = floor(position); if (pos+10) { a=buffer[(int)(floor(position))-1]; - + } else { a=buffer[0]; - + } - + b=buffer[(long) position]; if (positionstart && position < end-1) { a=buffer[(long) position+1]; - + } else { a=buffer[0]; - + } - + b=buffer[(long) position]; if (position>start) { c=buffer[(long) position-1]; - + } else { c=buffer[0]; - + } if (position>start+1) { d=buffer[(long) position-2]; - + } else { d=buffer[0]; } @@ -1047,15 +1114,15 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double a2 = a - 2.5 * b + 2.f * c - 0.5f * d; a3 = 0.5f * (d - a) + 1.5f * (b - c); output = (double) (((a3 * remainder + a2) * -remainder + a1) * -remainder + b) / 32767; - + } - + return(output); } void maxiSample::getLength() { - length=myDataSize*0.5; + length=myDataSize*0.5; } void maxiSample::setLength(unsigned long numSamples) { @@ -1084,76 +1151,76 @@ void maxiSample::reset() { -/* OK this compressor and gate are now ready to use. The envelopes, like all the envelopes in this recent update, use stupid algorithms for +/* 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 */ double maxiDyn::gate(double input, double threshold, long holdtime, double attack, double release) { - - if (fabs(input)>threshold && attackphase!=1){ + + if (fabs(input)>threshold && attackphase!=1){ holdcount=0; releasephase=0; attackphase=1; if(amplitude==0) amplitude=0.01; } - + if (attackphase==1 && amplitude<1) { amplitude*=(1+attack); output=input*amplitude; } - + if (amplitude>=1) { attackphase=0; holdphase=1; } - + if (holdcount0.) { output=input*(amplitude*=release); - + } - + return output; } double maxiDyn::compressor(double input, double ratio, double threshold, double attack, double release) { - - if (fabs(input)>threshold && attackphase!=1){ + + if (fabs(input)>threshold && attackphase!=1){ holdcount=0; releasephase=0; attackphase=1; if(currentRatio==0) currentRatio=ratio; } - + if (attackphase==1 && currentRatio=ratio-1) { attackphase=0; releasephase=1; } - + if (releasephase==1 && currentRatio>0.) { - currentRatio*=release; + currentRatio*=release; } - + if (input>0.) { output = input/(1.+currentRatio); } else { output = input/(1.+currentRatio); } - + return output*(1+log(ratio)); } @@ -1162,101 +1229,101 @@ double maxiDyn::compressor(double input, double ratio, double threshold, double It takes mental numbers for attack and release tho. Basically, they're exponentials. I'll map them out later so that it's a bit more intuitive */ double maxiEnv::ar(double input, double attack, double release, long holdtime, int trigger) { - - if (trigger==1 && attackphase!=1 && holdphase!=1){ + + if (trigger==1 && attackphase!=1 && holdphase!=1){ holdcount=0; releasephase=0; attackphase=1; } - + if (attackphase==1) { amplitude+=(1*attack); output=input*amplitude; } - + if (amplitude>=1) { amplitude=1; attackphase=0; holdphase=1; } - + if (holdcount0.) { output=input*(amplitude*=release); - + } - + return output; } /* 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){ + + 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); + output=input*(amplitude*=decay); if (amplitude<=sustain) { decayphase=0; holdphase=1; } } - + if (holdcount0.) { output=input*(amplitude*=release); - + } - + return output; } double convert::mtof(int midinote) { - + return mtofarray[midinote]; } @@ -1266,6 +1333,6 @@ void maxiEnvelopeFollower::setAttack(double attackMS) { } void maxiEnvelopeFollower::setRelease(double releaseMS) { - release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); + release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); } diff --git a/maximilian.h b/maximilian.h index 0cac59d9..fcb445a2 100755 --- a/maximilian.h +++ b/maximilian.h @@ -6,7 +6,7 @@ * Copyright 2009 Mick Grierson & Strangeloop Limited. All rights reserved. * Thanks to the Goldsmiths Creative Computing Team. * Special thanks to Arturo Castro for the PortAudio implementation. - * + * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without @@ -15,11 +15,11 @@ * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: - * + * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT @@ -63,15 +63,15 @@ class maxiSettings { class maxiOsc { - + double frequency; double phase; double startphase; double endphase; double output; double tri; - - + + public: maxiOsc(); double sinewave(double frequency); @@ -88,12 +88,12 @@ class maxiOsc { double sawn(double frequency); double rect(double frequency, double duty=0.5); void phaseReset(double phaseIn); - + }; class maxiEnvelope { - + double period; double output; double startval; @@ -101,12 +101,12 @@ class maxiEnvelope { double nextval; int isPlaying; -public: +public: double line(int numberofsegments,double segments[100]); void trigger(int index,double amp); int valindex; double amplitude; - + }; @@ -117,17 +117,17 @@ class maxiDelayline { double endphase; double output; double memory[88200]; - + public: maxiDelayline(); double dl(double input, int size, double feedback); double dl(double input, int size, double feedback, int position); - - + + }; -class maxiFilter { +class maxiFilter { double gain; double input; double output; @@ -138,7 +138,14 @@ class maxiFilter { double y;//pos double z;//pole double c;//filter coefficient - + + //stuff for iir filters + double alpha, w0, a0, b0, a1, b1, a2, b2; + double in1 = 0.0f; + double in2 = 0.0f; + double out1 = 0.0f; + double out2 = 0.0f; + public: maxiFilter():x(0.0), y(0.0), z(0.0), c(0.0){}; double cutoff; @@ -146,9 +153,11 @@ class maxiFilter { double lores(double input,double cutoff1, double resonance); double hires(double input,double cutoff1, double resonance); double bandpass(double input,double cutoff1, double resonance); + double peaking(double input, double centerFreq, double q, double gain); + double formant(double input, double centerFreq, double q, double gain); double lopass(double input,double cutoff); double hipass(double input,double cutoff); - + }; class maxiMix { @@ -163,35 +172,35 @@ class maxiMix { double *stereo(double input,double two[2],double x); double *quad(double input,double four[4], double x,double y); double *ambisonic(double input,double eight[8],double x,double y, double z); - + }; //lagging with an exponential moving average //a lower alpha value gives a slower lag -template +template class maxiLagExp { public: T alpha, alphaReciprocal; T val; - + maxiLagExp() { init(0.5, 0.0); }; - + maxiLagExp(T initAlpha, T initVal) { init(initAlpha, initVal); } - + void init(T initAlpha, T initVal) { alpha = initAlpha; alphaReciprocal = 1.0 - alpha; val = initVal; } - + inline void addSample(T newVal) { val = (alpha * newVal) + (alphaReciprocal * val); } - + inline T value() { return val; } @@ -199,7 +208,7 @@ class maxiLagExp { class maxiSample { - + private: string myPath; int myChunkSize; @@ -213,21 +222,21 @@ class maxiSample { double speed; double output; maxiLagExp loopRecordLag; - + public: int myDataSize; short myChannels; int mySampleRate; long length; void getLength(); - void setLength(unsigned long numSamples); - - + void setLength(unsigned long numSamples); + + char* myData; short* temp; - + // get/set for the Path property - + ~maxiSample() { if (myData) free(myData); @@ -235,21 +244,21 @@ class maxiSample { printf("freeing SampleData"); } - + maxiSample():myData(NULL),temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; - + bool load(string fileName, int channel=0); - + bool loadOgg(string filename,int channel=0); - + void trigger(); - + // read a wav file into this class bool read(); - + //read an ogg file into this class using stb_vorbis bool readOgg(); - + void loopRecord(double newSample, const bool recordEnabled, const double recordMix) { loopRecordLag.addSample(recordEnabled); if(recordEnabled) { @@ -262,42 +271,42 @@ class maxiSample { if (recordPosition == length) recordPosition=0; } - + 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); bool save() { 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.seekp (0, ios::beg); myFile.write ("RIFF", 4); myFile.write ((char*) &myChunkSize, 4); myFile.write ("WAVE", 4); @@ -312,10 +321,10 @@ class maxiSample { myFile.write ("data", 4); myFile.write ((char*) &myDataSize, 4); myFile.write (myData, myDataSize); - + return true; } - + // return a printable summary of the wav file char *getSummary() { @@ -332,31 +341,31 @@ class maxiMap { 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; } - + }; 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); @@ -374,8 +383,8 @@ class maxiDyn { }; 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); @@ -443,7 +452,7 @@ inline double maxiFlanger::flange(const double input, const unsigned int delay, //todo: needs fixing double output; double lfoVal = lfo.triangle(speed); - output = dl.dl(input, delay + (lfoVal * depth * delay) + 1, feedback) ; + output = dl.dl(input, delay + (lfoVal * depth * delay) + 1, feedback) ; double normalise = (1 - fabs(output)); output *= normalise; return (output + input) / 2.0; @@ -459,7 +468,7 @@ class maxiChorus { maxiDelayline dl, dl2; maxiOsc lfo; maxiFilter lopass; - + }; inline double maxiChorus::chorus(const double input, const unsigned int delay, const double feedback, const double speed, const double depth) @@ -468,8 +477,8 @@ inline double maxiChorus::chorus(const double input, const unsigned int delay, c double output1, output2; double lfoVal = lfo.noise(); lfoVal = lopass.lores(lfoVal, speed, 1.0) * 2.0; - output1 = dl.dl(input, delay + (lfoVal * depth * delay) + 1, feedback) ; - output2 = dl2.dl(input, (delay + (lfoVal * depth * delay * 1.02) + 1) * 0.98, feedback * 0.99) ; + output1 = dl.dl(input, delay + (lfoVal * depth * delay) + 1, feedback) ; + output2 = dl2.dl(input, (delay + (lfoVal * depth * delay * 1.02) + 1) * 0.98, feedback * 0.99) ; output1 *= (1.0 - fabs(output1)); output2 *= (1.0 - fabs(output2)); return (output1 + output2 + input) / 3.0; @@ -489,7 +498,7 @@ class maxiEnvelopeFollower { 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;} diff --git a/ofxMaxim/ofxMaxim/libs/maximilian.cpp b/ofxMaxim/ofxMaxim/libs/maximilian.cpp index 18b2af22..720bcae7 100644 --- a/ofxMaxim/ofxMaxim/libs/maximilian.cpp +++ b/ofxMaxim/ofxMaxim/libs/maximilian.cpp @@ -16,11 +16,11 @@ * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: - * + * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT @@ -34,7 +34,7 @@ #include "maximilian.h" #include "math.h" -/* Maximilian can be configured to load ogg vorbis format files using the +/* Maximilian can be configured to load ogg vorbis format files using the * loadOgg() method. * 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*/ @@ -55,7 +55,7 @@ int maxiSettings::channels = 2; int maxiSettings::bufferSize = 1024; -//this is a 514-point sinewave table that has many uses. +//this is a 514-point sinewave table that has many uses. 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 }; @@ -200,7 +200,7 @@ double mtofarray[129]={0, 8.661957, 9.177024, 9.722718, 10.3, 10.913383, 11.5623 void setup();//use this to do any initialisation if you want. -void play(double *channels);//run dac! +void play(double *channels);//run dac! maxiOsc::maxiOsc(){ //When you create an oscillator, the constructor sets the phase of the oscillator to 0. @@ -218,7 +218,7 @@ double maxiOsc::noise() { void maxiOsc::phaseReset(double phaseIn) { //This allows you to set the phase of the oscillator to anything you like. phase=phaseIn; - + } double maxiOsc::sinewave(double frequency) { @@ -227,7 +227,7 @@ double maxiOsc::sinewave(double frequency) { if ( phase >= 1.0 ) phase -= 1.0; phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); - + } double maxiOsc::sinebuf4(double frequency) { @@ -237,21 +237,21 @@ double maxiOsc::sinebuf4(double frequency) { phase += 512./(maxiSettings::sampleRate/(frequency)); if ( phase >= 511 ) phase -=512; remainder = phase - floor(phase); - + if (phase==0) { a=sineBuffer[(long) 512]; b=sineBuffer[(long) phase]; c=sineBuffer[(long) phase+1]; d=sineBuffer[(long) phase+2]; - + } else { a=sineBuffer[(long) phase-1]; b=sineBuffer[(long) phase]; c=sineBuffer[(long) phase+1]; d=sineBuffer[(long) phase+2]; - + } - + a1 = 0.5f * (c - a); a2 = a - 2.5 * b + 2.f * c - 0.5f * d; a3 = 0.5f * (d - a) + 1.5f * (b - c); @@ -275,16 +275,16 @@ double maxiOsc::coswave(double frequency) { if ( phase >= 1.0 ) phase -= 1.0; phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); - + } double maxiOsc::phasor(double frequency) { - //This produces a floating point linear ramp between 0 and 1 at the desired frequency + //This produces a floating point linear ramp between 0 and 1 at the desired frequency output=phase; if ( phase >= 1.0 ) phase -= 1.0; phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); -} +} double maxiOsc::square(double frequency) { //This is a square wave @@ -307,7 +307,7 @@ double maxiOsc::pulse(double frequency, double duty) { } double maxiOsc::phasor(double frequency, double startphase, double endphase) { - //This is a phasor that takes a value for the start and end of the ramp. + //This is a phasor that takes a value for the start and end of the ramp. output=phase; if (phase= 1.0 ) phase -= 2.0; phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); - -} + +} double maxiOsc::triangle(double frequency) { //This is a triangle wave. @@ -337,7 +337,7 @@ double maxiOsc::triangle(double frequency) { output =((1.0-phase) - 0.25) * 4; } return(output); - + } double maxiOsc::sawn(double frequency) { @@ -356,7 +356,7 @@ double maxiOsc::sawn(double frequency) { double remainder = temp - floor(temp); output = (double) ((1.0f-remainder) * transition[(long)temp] + remainder * transition[1+(long)temp]) - phase; return(output); - + } @@ -378,11 +378,11 @@ double maxiEnvelope::line(int numberofsegments,double segments[1000]) { startval=currentval; } output=amplitude; - + } else { output=0; - + } return(output); } @@ -392,7 +392,7 @@ void maxiEnvelope::trigger(int index, double amp) { isPlaying=1;//ok the envelope is being used now. valindex=index; amplitude=amp; - + } //Delay with feedback @@ -410,7 +410,7 @@ double maxiDelayline::dl(double input, int size, double feedback) { memory[phase]=(memory[phase]*feedback)+(input*feedback)*0.5; phase+=1; return(output); - + } double maxiDelayline::dl(double input, int size, double feedback, int position) { @@ -420,7 +420,7 @@ double maxiDelayline::dl(double input, int size, double feedback, int position) memory[phase]=(memory[phase]*feedback)+(input*feedback)*chandiv; phase+=1; return(output); - + } //I particularly like these. cutoff between 0 and 1 @@ -476,13 +476,80 @@ double maxiFilter::bandpass(double input,double cutoff1, double resonance) { inputs[0] = (1-resonance)*(sqrt(resonance*(resonance-4.0*pow(z,2.0)+2.0)+1)); inputs[1] = 2*z*resonance; inputs[2] = pow((resonance*-1),2); - + output=inputs[0]*input+inputs[1]*outputs[1]+inputs[2]*outputs[2]; outputs[2]=outputs[1]; outputs[1]=output; return(output); } +//Peaking filter (emphasizes a certain frequency band) +//based on the cookbook by Robert Bristow-Johnson +//http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +//implemented by Jakub Fiala +//q ranges from 0 to 1 +//so does gain + +double maxiFilter::peaking(double input, double centerFreq, double q, double gain) { + cutoff = centerFreq; + gain *= 4; + //calculate poles and coeffs + w0 = TWOPI * cutoff / 44100; + alpha = sin(w0)/(2*q); + b0 = 1 + alpha*gain; + b1 = -2*cos(w0); + b2 = 1 - alpha*gain; + a0 = 1 + alpha/gain; + a1 = -2*cos(w0); + a2 = 1 - alpha/gain; + + //apply filter + output = (b0/a0)*input + (b1/a0)*in1 + (b2/a0)*in2 - (a1/a0)*out1 - (a2/a0)*out2; + + //update memory + out2 = out1; + out1 = output; + in2 = in1; + in1 = input; + + return output; +} + +//Formant filter (basically a resonant bandpass) +//based on the cookbook by Robert Bristow-Johnson +//http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +//implemented by Jakub Fiala +//q ranges from 0 to 1 +//so does gain + +double maxiFilter::formant(double input, double centerFreq, double q, double gain) { + cutoff = centerFreq; + + //offset gain + gain += 1; + //gain *= 4; + //calculate poles and coeffs + w0 = TWOPI * cutoff / 44100; + alpha = sin(w0)/(20*q); + b0 = alpha*gain; + b1 = 0; + b2 = -alpha*gain; + a0 = 1 + alpha/gain; + a1 = -2*cos(w0); + a2 = 1 - alpha/gain; + + //apply filter + output = (b0/a0)*input + (b1/a0)*in1 + (b2/a0)*in2 - (a1/a0)*out1 - (a2/a0)*out2; + + //update memory + out2 = out1; + out1 = output; + in2 = in1; + in1 = input; + + return output; +} + //stereo bus double *maxiMix::stereo(double input,double two[2],double x) { if (x>1) x=1; @@ -490,7 +557,7 @@ double *maxiMix::stereo(double input,double two[2],double x) { two[0]=input*sqrt(1.0-x); two[1]=input*sqrt(x); return(two); -} +} //quad bus double *maxiMix::quad(double input,double four[4],double x,double y) { @@ -544,7 +611,7 @@ bool maxiSample::loadOgg(string fileName, int channel) { myChannels=(short)channelx; length=myDataSize; mySampleRate=44100; - + if (myChannels>1) { int position=0; int channel=readChannel; @@ -572,28 +639,28 @@ bool maxiSample::read() bool datafound = false; inFile.seekg(4, ios::beg); inFile.read( (char*) &myChunkSize, 4 ); // read the ChunkSize - + inFile.seekg(16, ios::beg); inFile.read( (char*) &mySubChunk1Size, 4 ); // read the SubChunk1Size - + //inFile.seekg(20, ios::beg); inFile.read( (char*) &myFormat, sizeof(short) ); // read the file format. This should be 1 for PCM - + //inFile.seekg(22, ios::beg); inFile.read( (char*) &myChannels, sizeof(short) ); // read the # of channels (1 or 2) - + //inFile.seekg(24, ios::beg); inFile.read( (char*) &mySampleRate, sizeof(int) ); // read the samplerate - + //inFile.seekg(28, ios::beg); inFile.read( (char*) &myByteRate, sizeof(int) ); // read the byterate - + //inFile.seekg(32, ios::beg); inFile.read( (char*) &myBlockAlign, sizeof(short) ); // read the blockalign - + //inFile.seekg(34, ios::beg); inFile.read( (char*) &myBitsPerSample, sizeof(short) ); // read the bitspersample - + //ignore any extra chunks char chunkID[5]=""; chunkID[4] = 0; @@ -610,14 +677,14 @@ bool maxiSample::read() filePos += myDataSize; } } - + // read the data chunk char *myData = (char*) malloc(myDataSize * sizeof(char)); inFile.seekg(filePos, ios::beg); inFile.read(myData, myDataSize); length=myDataSize*(0.5/myChannels); inFile.close(); // close the input file - + cout << "Ch: " << myChannels << ", len: " << length << endl; if (myChannels>1) { int position=0; @@ -631,16 +698,16 @@ bool maxiSample::read() free(temp); temp = (short*) malloc(myDataSize * sizeof(char)); memcpy(temp, myData, myDataSize * sizeof(char)); - + free(myData); - + }else { -// cout << "ERROR: Could not load sample: " <=0) { - + if ((long) position>=length-1) position=1; remainder = position - floor(position); if (position+1=0) { a=position-1; - + } else { a=0; @@ -736,7 +803,7 @@ double maxiSample::play(double speed) { b=0; } output = (double) ((-1-remainder) * temp[a] + remainder * temp[b])/32767;//linear interpolation - } + } return(output); } @@ -753,14 +820,14 @@ double maxiSample::play(double frequency, double start, double end, double &pos) if (pos= end ) pos = start; pos += ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = pos - floor(pos); long posl = floor(pos); if (posl+1start && position < end-1) { a=temp[(long) position+1]; - + } else { a=temp[0]; - + } - + b=temp[(long) position]; if (position>start) { c=temp[(long) position-1]; - + } else { c=temp[0]; - + } if (position>start+1) { d=temp[(long) position-2]; - + } else { d=temp[0]; } @@ -870,9 +937,9 @@ double maxiSample::play4(double frequency, double start, double end) { a2 = a - 2.5 * b + 2.f * c - 0.5f * d; a3 = 0.5f * (d - a) + 1.5f * (b - c); output = (double) (((a3 * remainder + a2) * -remainder + a1) * -remainder + b) / 32767; - + } - + return(output); } @@ -892,12 +959,12 @@ double maxiSample::bufferPlay(unsigned char &bufferin,double speed,long length) short* buffer = (short *)&bufferin; position=position+((speed*chandiv)/(maxiSettings::sampleRate/mySampleRate)); if (speed >=0) { - + if ((long) position>=length-1) position=1; remainder = position - floor(position); if (position+1=0) { a=position-1; - + } else { a=0; @@ -928,7 +995,7 @@ double maxiSample::bufferPlay(unsigned char &bufferin,double speed,long length) b=0; } output = (double) ((-1-remainder) * buffer[a] + remainder * buffer[b])/32767;//linear interpolation - } + } return(output); } @@ -941,14 +1008,14 @@ double maxiSample::bufferPlay(unsigned char &bufferin,double frequency, double s if (position= end ) position = start; position += ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); long pos = floor(position); if (pos+10) { a=buffer[(int)(floor(position))-1]; - + } else { a=buffer[0]; - + } - + b=buffer[(long) position]; if (positionstart && position < end-1) { a=buffer[(long) position+1]; - + } else { a=buffer[0]; - + } - + b=buffer[(long) position]; if (position>start) { c=buffer[(long) position-1]; - + } else { c=buffer[0]; - + } if (position>start+1) { d=buffer[(long) position-2]; - + } else { d=buffer[0]; } @@ -1058,15 +1125,15 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double a2 = a - 2.5 * b + 2.f * c - 0.5f * d; a3 = 0.5f * (d - a) + 1.5f * (b - c); output = (double) (((a3 * remainder + a2) * -remainder + a1) * -remainder + b) / 32767; - + } - + return(output); } void maxiSample::getLength() { - length=myDataSize*0.5; + length=myDataSize*0.5; } void maxiSample::setLength(unsigned long numSamples) { @@ -1107,7 +1174,7 @@ void maxiSample::normalise(float maxLevel) { } void maxiSample::autoTrim(float alpha, float threshold, bool trimStart, bool trimEnd) { - + int startMarker=0; if(trimStart) { maxiLagExp startLag(alpha, 0); @@ -1119,7 +1186,7 @@ void maxiSample::autoTrim(float alpha, float threshold, bool trimStart, bool tri startMarker++; } } - + int endMarker = length-1; if(trimEnd) { maxiLagExp endLag(alpha, 0); @@ -1131,9 +1198,9 @@ void maxiSample::autoTrim(float alpha, float threshold, bool trimStart, bool tri endMarker--; } } - + cout << "Autotrim: start: " << startMarker << ", end: " << endMarker << endl; - + int newLength = endMarker - startMarker; if (newLength > 0) { short *newData = (short*) malloc(sizeof(short) * newLength); @@ -1164,76 +1231,76 @@ 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 +/* 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 */ double maxiDyn::gate(double input, double threshold, long holdtime, double attack, double release) { - - if (fabs(input)>threshold && attackphase!=1){ + + if (fabs(input)>threshold && attackphase!=1){ holdcount=0; releasephase=0; attackphase=1; if(amplitude==0) amplitude=0.01; } - + if (attackphase==1 && amplitude<1) { amplitude*=(1+attack); output=input*amplitude; } - + if (amplitude>=1) { attackphase=0; holdphase=1; } - + if (holdcount0.) { output=input*(amplitude*=release); - + } - + return output; } double maxiDyn::compressor(double input, double ratio, double threshold, double attack, double release) { - - if (fabs(input)>threshold && attackphase!=1){ + + if (fabs(input)>threshold && attackphase!=1){ holdcount=0; releasephase=0; attackphase=1; if(currentRatio==0) currentRatio=ratio; } - + if (attackphase==1 && currentRatio=ratio-1) { attackphase=0; releasephase=1; } - + if (releasephase==1 && currentRatio>0.) { - currentRatio*=release; + currentRatio*=release; } - + if (input>0.) { output = input/(1.+currentRatio); } else { output = input/(1.+currentRatio); } - + return output*(1+log(ratio)); } @@ -1242,101 +1309,101 @@ double maxiDyn::compressor(double input, double ratio, double threshold, double It takes mental numbers for attack and release tho. Basically, they're exponentials. I'll map them out later so that it's a bit more intuitive */ double maxiEnv::ar(double input, double attack, double release, long holdtime, int trigger) { - - if (trigger==1 && attackphase!=1 && holdphase!=1){ + + if (trigger==1 && attackphase!=1 && holdphase!=1){ holdcount=0; releasephase=0; attackphase=1; } - + if (attackphase==1) { amplitude+=(1*attack); output=input*amplitude; } - + if (amplitude>=1) { amplitude=1; attackphase=0; holdphase=1; } - + if (holdcount0.) { output=input*(amplitude*=release); - + } - + return output; } /* and here's a new 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){ + + 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); + output=input*(amplitude*=decay); if (amplitude<=sustain) { decayphase=0; holdphase=1; } } - + if (holdcount0.) { output=input*(amplitude*=release); - + } - + return output; } double convert::mtof(int midinote) { - + return mtofarray[midinote]; } diff --git a/ofxMaxim/ofxMaxim/libs/maximilian.h b/ofxMaxim/ofxMaxim/libs/maximilian.h index 03dfea59..5a7fde4e 100755 --- a/ofxMaxim/ofxMaxim/libs/maximilian.h +++ b/ofxMaxim/ofxMaxim/libs/maximilian.h @@ -6,7 +6,7 @@ * Copyright 2009 Mick Grierson & Strangeloop Limited. All rights reserved. * Thanks to the Goldsmiths Creative Computing Team. * Special thanks to Arturo Castro for the PortAudio implementation. - * + * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without @@ -15,11 +15,11 @@ * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: - * + * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT @@ -69,15 +69,15 @@ class maxiSettings { class maxiOsc { - + double frequency; double phase; double startphase; double endphase; double output; double tri; - - + + public: maxiOsc(); double sinewave(double frequency); @@ -92,13 +92,13 @@ class maxiOsc { double sinebuf(double frequency); double sinebuf4(double frequency); void phaseReset(double phaseIn); - double sawn(double frequency); - + double sawn(double frequency); + }; class maxiEnvelope { - + double period; double output; double startval; @@ -106,12 +106,12 @@ class maxiEnvelope { double nextval; int isPlaying; -public: +public: double line(int numberofsegments,double segments[100]); void trigger(int index,double amp); int valindex; double amplitude; - + }; @@ -122,17 +122,17 @@ class maxiDelayline { double endphase; double output; double memory[88200]; - + public: maxiDelayline(); double dl(double input, int size, double feedback); double dl(double input, int size, double feedback, int position); - - + + }; -class maxiFilter { +class maxiFilter { double gain; double input; double output; @@ -143,7 +143,14 @@ class maxiFilter { double y;//pos double z;//pole double c;//filter coefficient - + + //stuff for iir filters + double alpha, w0, a0, b0, a1, b1, a2, b2; + double in1 = 0.0f; + double in2 = 0.0f; + double out1 = 0.0f; + double out2 = 0.0f; + public: maxiFilter():x(0.0), y(0.0), z(0.0), c(0.0){}; double cutoff; @@ -151,9 +158,11 @@ class maxiFilter { double lores(double input,double cutoff1, double resonance); double hires(double input,double cutoff1, double resonance); double bandpass(double input,double cutoff1, double resonance); + double peaking(double input, double centerFreq, double q, double gain); + double formant(double input, double centerFreq, double q, double gain); double lopass(double input,double cutoff); double hipass(double input,double cutoff); - + }; class maxiMix { @@ -168,35 +177,35 @@ class maxiMix { double *stereo(double input,double two[2],double x); double *quad(double input,double four[4], double x,double y); double *ambisonic(double input,double eight[8],double x,double y, double z); - + }; //lagging with an exponential moving average //a lower alpha value gives a slower lag -template +template class maxiLagExp { public: T alpha, alphaReciprocal; T val; - + maxiLagExp() { init(0.5, 0.0); }; - + maxiLagExp(T initAlpha, T initVal) { init(initAlpha, initVal); } - + void init(T initAlpha, T initVal) { alpha = initAlpha; alphaReciprocal = 1.0 - alpha; val = initVal; } - + inline void addSample(T newVal) { val = (alpha * newVal) + (alphaReciprocal * val); } - + inline T value() { return val; } @@ -204,7 +213,7 @@ class maxiLagExp { class maxiSample { - + private: string myPath; int myChunkSize; @@ -217,7 +226,7 @@ class maxiSample { double speed; double output; maxiLagExp loopRecordLag; - + public: double position, recordPosition; int myDataSize; @@ -225,22 +234,22 @@ class maxiSample { int mySampleRate; long length; void getLength(); - void setLength(unsigned long numSamples); - - + void setLength(unsigned long numSamples); + + // char* myData; short* temp; - + // get/set for the Path property - + ~maxiSample() { // if (myData) free(myData); if (temp) free(temp); } - + maxiSample():temp(NULL),position(0), recordPosition(0), myChannels(1), mySampleRate(maxiSettings::sampleRate) {}; - + maxiSample& operator=(const maxiSample &source) { if (this == &source) return *this; @@ -255,19 +264,19 @@ class maxiSample { length = source.length; return *this; } - + bool load(string fileName, int channel=0); - + bool loadOgg(string filename,int channel=0); - + void trigger(); - + // read a wav file into this class bool read(); - + //read an ogg file into this class using stb_vorbis bool readOgg(); - + 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; @@ -281,48 +290,48 @@ class maxiSample { if (recordPosition >= end * length) recordPosition= start * length; } - + void clear(); - + void reset(); - + 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.seekp (0, ios::beg); myFile.write ("RIFF", 4); myFile.write ((char*) &myChunkSize, 4); myFile.write ("WAVE", 4); @@ -337,10 +346,10 @@ class maxiSample { myFile.write ("data", 4); myFile.write ((char*) &myDataSize, 4); myFile.write ((char*) temp, myDataSize); - + return true; } - + // return a printable summary of the wav file char *getSummary() { @@ -348,7 +357,7 @@ class maxiSample { 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); 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 }; @@ -360,19 +369,19 @@ class maxiMap { 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(v, l, h); template static T inline clamp(T v, const T low, const T high) { @@ -383,12 +392,12 @@ class maxiMap { } 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); @@ -406,8 +415,8 @@ class maxiDyn { }; 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); @@ -475,7 +484,7 @@ inline double maxiFlanger::flange(const double input, const unsigned int delay, //todo: needs fixing double output; double lfoVal = lfo.triangle(speed); - output = dl.dl(input, delay + (lfoVal * depth * delay) + 1, feedback) ; + output = dl.dl(input, delay + (lfoVal * depth * delay) + 1, feedback) ; double normalise = (1 - fabs(output)); output *= normalise; return (output + input) / 2.0; @@ -491,7 +500,7 @@ class maxiChorus { maxiDelayline dl, dl2; maxiOsc lfo; maxiFilter lopass; - + }; inline double maxiChorus::chorus(const double input, const unsigned int delay, const double feedback, const double speed, const double depth) @@ -500,8 +509,8 @@ inline double maxiChorus::chorus(const double input, const unsigned int delay, c double output1, output2; double lfoVal = lfo.noise(); lfoVal = lopass.lores(lfoVal, speed, 1.0) * 2.0; - output1 = dl.dl(input, delay + (lfoVal * depth * delay) + 1, feedback) ; - output2 = dl2.dl(input, (delay + (lfoVal * depth * delay * 1.02) + 1) * 0.98, feedback * 0.99) ; + output1 = dl.dl(input, delay + (lfoVal * depth * delay) + 1, feedback) ; + output2 = dl2.dl(input, (delay + (lfoVal * depth * delay * 1.02) + 1) * 0.98, feedback * 0.99) ; output1 *= (1.0 - fabs(output1)); output2 *= (1.0 - fabs(output2)); return (output1 + output2 + input) / 3.0; @@ -516,17 +525,17 @@ class maxiEnvelopeFollowerType { 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;} @@ -553,37 +562,37 @@ class maxiDCBlocker { /* 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; @@ -599,7 +608,7 @@ class maxiSVF { notch = w-k*v1; return (low * lpmix) + (band * bpmix) + (high * hpmix) + (notch * notchmix); } - + private: inline void setParams(double _freq, double _res) { freq = _freq; @@ -613,10 +622,10 @@ class maxiSVF { g3 = g * ginv; g4 = 2.0 * ginv; } - + double v0z, v1, v2, g, damping, k, ginv, g1, g2, g3 ,g4; double freq, res; - + };