/* * maximilian.cpp * platform independent synthesis library using portaudio or rtaudio * * Created by Mick Grierson on 29/12/2009. * 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 * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * 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, * 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 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * */ #include "maxi_emscr.h" #include "maxi_embind.h" #include "big_arrays.h" //#include "math.h" /* 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*/ //#define VORBIS //#ifdef VORBIS //extern "C" { // #include "stb_vorbis.h" //} //#endif //This used to be important for dealing with multichannel playback float chandiv= 1; int maxiSettings::sampleRate = 44100; int maxiSettings::channels = 2; int maxiSettings::bufferSize = 1024; //void setup();//use this to do any initialisation if you want. //void play(double *channels);//run dac! // -------------------------------------------------------------------------------- // MAXI OSC maxiOsc::maxiOsc(){ //When you create an oscillator, the constructor sets the phase of the oscillator to 0. phase = 0.0; } double maxiOsc::noise() { //White Noise //always the same unless you seed it. float r = rand()/(float)RAND_MAX; output=r*2-1; return(output); } 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) { //This is a sinewave oscillator output=sin (phase*(TWOPI)); if ( phase >= 1.0 ) phase -= 1.0; phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); } double maxiOsc::sinebuf4(double frequency) { //This is a sinewave oscillator that uses 4 point interpolation on a 514 point buffer double remainder; double a,b,c,d,a1,a2,a3; 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); output = double (((a3 * remainder + a2) * remainder + a1) * remainder + b); return(output); } double maxiOsc::sinebuf(double frequency) { //specify the frequency of the oscillator in Hz / cps etc. //This is a sinewave oscillator that uses linear interpolation on a 514 point buffer double remainder; phase += 512./(maxiSettings::sampleRate/(frequency*chandiv)); if ( phase >= 511 ) phase -=512; remainder = phase - floor(phase); output = (double) ((1-remainder) * sineBuffer[1+ (long) phase] + remainder * sineBuffer[2+(long) phase]); return(output); } double maxiOsc::coswave(double frequency) { //This is a cosine oscillator output=cos (phase*(TWOPI)); 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 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 if (phase<0.5) output=-1; if (phase>0.5) output=1; if ( phase >= 1.0 ) phase -= 1.0; phase += (1./(maxiSettings::sampleRate/(frequency))); return(output); } double maxiOsc::pulse(double frequency, double duty) { //This is a pulse generator that creates a signal between -1 and 1. if (duty<0.) duty=0; if (duty>1.) duty=1; if ( phase >= 1.0 ) phase -= 1.0; phase += (1./(maxiSettings::sampleRate/(frequency))); if (phaseduty) output=1.; return(output); } 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. output=phase; if (phase= endphase ) phase = startphase; phase += ((endphase-startphase)/(maxiSettings::sampleRate/(frequency))); return(output); } double maxiOsc::saw(double frequency) { //Sawtooth generator. This is like a phasor but goes between -1 and 1 output=phase; if ( phase >= 1.0 ) phase -= 2.0; 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. 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); } // -------------------------------------------------------------------------------- // MAXI ENVELOPE double maxiEnvelope::line(int numberofsegments, std::vector& segments) { //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); nextval=segments[valindex+2]; currentval=segments[valindex]; if (currentval-amplitude > 0.0000001 && valindex < numberofsegments) { amplitude += ((currentval-startval)/(maxiSettings::sampleRate/period)); } else if (currentval-amplitude < -0.0000001 && valindex < numberofsegments) { amplitude -= (((currentval-startval)*(-1))/(maxiSettings::sampleRate/period)); } else if (valindex >numberofsegments-1) { valindex=numberofsegments-2; } else { valindex=valindex+2; startval=currentval; } output=amplitude; } else { output=0; } return(output); } //and this void maxiEnvelope::trigger(int index, double amp) { isPlaying=1;//ok the envelope is being used now. valindex=index; SetAmplitude(amp); // amplitude=amp; } /* // -------------------------------------------------------------------------------- // MAXI DELAY LINE //Delay with feedback maxiDelayline::maxiDelayline() { memset( memory, 0, 88200*sizeof (double) ); } double maxiDelayline::dl(double input, int size, double feedback) { if ( phase >=size ) { phase = 0; } output=memory[phase]; memory[phase]=(memory[phase]*feedback)+(input*feedback)*0.5; phase+=1; return(output); } double maxiDelayline::dl(double input, int size, double feedback, int position) { if ( phase >=size ) phase = 0; if ( position >=size ) position = 0; output=memory[position]; memory[phase]=(memory[phase]*feedback)+(input*feedback)*chandiv; phase+=1; return(output); } */ // -------------------------------------------------------------------------------- // MAXI FILTER //I particularly like these. cutoff between 0 and 1 double maxiFilter::lopass(double input, double cutoff) { output=outputs[0] + cutoff*(input-outputs[0]); outputs[0]=output; return(output); } //as above double maxiFilter::hipass(double input, double cutoff) { output=input-(outputs[0] + cutoff*(input-outputs[0])); outputs[0]=output; return(output); } //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; if (cutoff<10) cutoff=10; if (cutoff>(maxiSettings::sampleRate*0.5)) cutoff=(maxiSettings::sampleRate*0.5); if (resonance<1.) resonance = 1.; z=cos(TWOPI*cutoff/maxiSettings::sampleRate); c=2-2*z; double r=(sqrt(2.0)*sqrt(-pow((z-1.0),3.0))+resonance*(z-1))/(resonance*(z-1)); x=x+(input-y)*c; y=y+x; x=x*r; output=y; return(output); } //working hires filter double maxiFilter::hires(double input,double cutoff1, double resonance) { cutoff=cutoff1*0.5; if (cutoff<10) cutoff=10; if (cutoff>(maxiSettings::sampleRate*0.5)) cutoff=(maxiSettings::sampleRate*0.5); if (resonance<1.) resonance = 1.; z=cos(TWOPI*cutoff/maxiSettings::sampleRate); c=2-2*z; double r=(sqrt(2.0)*sqrt(-pow((z-1.0),3.0))+resonance*(z-1))/(resonance*(z-1)); x=x+(input-y)*c; y=y+x; x=x*r; output=input-y; return(output); } //This works a bit. Needs attention. double maxiFilter::bandpass(double input,double cutoff1, double resonance) { cutoff=cutoff1; if (cutoff>(maxiSettings::sampleRate*0.5)) cutoff=(maxiSettings::sampleRate*0.5); if (resonance>=1.) resonance=0.999999; z=cos(TWOPI*cutoff/maxiSettings::sampleRate); 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); } /* // -------------------------------------------------------------------------------- // MAXI MIX //stereo bus double *maxiMix::stereo(double input,double two[2],double x) { if (x>1) x=1; if (x<0) x=0; 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) { if (x>1) x=1; if (x<0) x=0; if (y>1) y=1; if (y<0) y=0; four[0]=input*sqrt((1.0-x)*y); four[1]=input*sqrt((1.0-x)*(1.0-y)); four[2]=input*sqrt(x*y); four[3]=input*sqrt(x*(1.0-y)); return(four); } //ambisonic bus double *maxiMix::ambisonic(double input,double eight[8],double x,double y,double z) { if (x>1) x=1; if (x<0) x=0; if (y>1) y=1; if (y<0) y=0; if (z>1) y=1; if (z<0) y=0; eight[0]=input*(sqrt((1.0-x)*y)*1.0-z); eight[1]=input*(sqrt((1.0-x)*(1.0-y))*1.0-z); eight[2]=input*(sqrt(x*y)*1.0-z); eight[3]=input*(sqrt(x*(1.0-y))*1.0-z); eight[4]=input*(sqrt((1.0-x)*y)*z); eight[5]=input*(sqrt((1.0-x)*(1.0-y))*z); eight[6]=input*sqrt((x*y)*z); eight[7]=input*sqrt((x*(1.0-y))*z); return(eight); } */ // -------------------------------------------------------------------------------- // MAXI SAMPLE //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; myDataSize = stb_vorbis_decode_filename(const_cast(fileName.c_str()), &channelx, &temp); result = myDataSize > 0; printf("\nchannels = %d\nlength = %d",channelx,myDataSize); printf("\n"); myChannels=(short)channelx; length=myDataSize; mySampleRate=44100; if (myChannels>1) { int position=0; int channel=readChannel; for (int i=channel;i1) { int position=0; int channel=readChannel*2; for (int i=channel;i& temp){ tempDC = temp; length = tempDC.size(); } //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=0) { if ((long) position>=length-1) position=1; remainder = position - floor(position); if (position+1=0) { a=position-1; } else { a=0; } if (position-2>=0) { b=position-2; } else { b=0; } output = (double) ((-1-remainder) * tempDC.at(a) + remainder * tempDC.at(b));//linear interpolation } 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; long a,b; if (frequency >0.) { if (pos= end ) pos = start; pos += ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = pos - floor(pos); long posl = floor(pos); if (posl+1=0) { a=posl-1; } else { a=0; } if (posl-2>=0) { b=posl-2; } else { b=0; } output = (double) ((-1-remainder) * tempDC.at(a) + remainder * tempDC.at(b));//linear interpolation } return(output); } //Same as above. better cubic inerpolation. Cobbled together from various (pd externals, yehar, other places). // needs fixing for online version double maxiSample::play4(double frequency, double start, double end) { double remainder; double a,b,c,d,a1,a2,a3; if (frequency > 0.) { if (position= end ) position = start; position += ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); if (position>0) { a=tempDC.at((long)(floor(position))-1); } else { a=tempDC.at(0); } b=tempDC.at((long) position); if (positionstart && position < end-1) { a=tempDC.at((long) position+1); } else { a=tempDC.at(0); } b=tempDC.at((long) position); if (position>start) { c=tempDC.at((long) position-1); } else { c=tempDC.at(0); } if (position>start+1) { d=tempDC.at((long) position-2); } else { d=tempDC.at(0); } 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); output = (double) (((a3 * remainder + a2) * -remainder + a1) * -remainder + b); } 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; position=(position+1); remainder = position - (long) position; if ((long) position>length) position=0; output = (double) ((1-remainder) * buffer[1+ (long) position] + remainder * buffer[2+(long) position])/32767;//linear interpolation return(output); } double maxiSample::bufferPlay(unsigned char &bufferin,double speed,long length) { double remainder; long a,b; 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; } if (position-2>=0) { b=position-2; } else { b=0; } output = (double) ((-1-remainder) * buffer[a] + remainder * buffer[b])/32767;//linear interpolation } return(output); } double maxiSample::bufferPlay(unsigned char &bufferin,double frequency, double start, double end) { double remainder; length=end; long a,b; short* buffer = (short *)&bufferin; if (frequency >0.) { if (position= end ) position = start; position += ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); long pos = floor(position); if (pos+1=0) { a=pos-1; } else { a=0; } if (pos-2>=0) { b=pos-2; } else { b=0; } output = (double) ((-1-remainder) * buffer[a] + remainder * buffer[b])/32767;//linear interpolation } return(output); } //better cubic inerpolation. Cobbled together from various (pd externals, yehar, other places). double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double start, double end) { double remainder; double a,b,c,d,a1,a2,a3; short* buffer = (short*)&bufferin; if (frequency >0.) { if (position= end ) position = start; position += ((end-start)/(maxiSettings::sampleRate/(frequency*chandiv))); remainder = position - floor(position); if (position>0) { 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]; } 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); output = (double) (((a3 * remainder + a2) * -remainder + a1) * -remainder + b) / 32767; } return(output); } void maxiSample::getLength() { length=myDataSize*0.5; } void maxiSample::setLength(unsigned long numSamples) { 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; recordPosition=0; } void maxiSample::clear() { memset(myData, 0, myDataSize); } void maxiSample::reset() { position=0; } */ /* 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){ 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){ 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; } 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. 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){ 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){ 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 (holdcount0.) { output=input*(amplitude*=release); } return output; } double convert::mtof(int midinote) { return mtofarray[midinote]; } void maxiEnvelopeFollower::setAttack(double attackMS) { attack = pow( 0.01, 1.0 / ( attackMS * maxiSettings::sampleRate * 0.001 ) ); } void maxiEnvelopeFollower::setRelease(double releaseMS) { release = pow( 0.01, 1.0 / ( releaseMS * maxiSettings::sampleRate * 0.001 ) ); } */