Here is the example we will be looking at in class w9 Tuesday.
Tom
Here is the updated fuzztrem plugin with 3 parameters and controls added ClassTest1126.
Sample code to parse MIDI – noteOff, noteOn and bendPitch are my own methods – you will need to write your own versions of these methods to connect the MIDI data to your process.
MidiBuffer::Iterator it(midiMessages); MidiMessage msg(0x80,0,0,0); int pos; int32_t note; // start with the MIDI processing while(it.getNextEvent(msg , pos)) { if(msg.isNoteOn()) { note = msg.getNoteNumber(); noteOn(note); } if(msg.isNoteOff()) { note = msg.getNoteNumber(); noteOff(note); } if(msg.isPitchWheel()) { bendPitch(msg.getPitchWheelValue()); } }
Simple code to save your current settings to a session (pulled from my plugin ++pitchsift)
void PitchsiftAudioProcessor::getStateInformation (MemoryBlock& destData) { // You should use this method to store your parameters in the memory block. // You could do that either as raw data, or use the XML or ValueTree classes // as intermediaries to make it easy to save and load complex data. ScopedPointerxml (parameters.state.createXml()); copyXmlToBinary (*xml, destData); } void PitchsiftAudioProcessor::setStateInformation (const void* data, int sizeInBytes) { // You should use this method to restore your parameters from this memory block, // whose contents will have been created by the getStateInformation() call. // This getXmlFromBinary() helper function retrieves our XML from the binary blob.. ScopedPointer xmlState (getXmlFromBinary (data, sizeInBytes)); }
1) take one of your externals from assignment 3, and turn it into a VST plugin or JUCE standalone.
2) make a block diagram of your proposed final project
due the tuesday of week 9.
This week we will be getting started in using JUCE to create audio plugins. I will show you both the audio processing and graphic layers, and how they connect. To start, you need to download the JUCE SDK from www.juce.com. Join with either the Education or Personal license.
Along with these lessons, you should look at the following youtube videos from The Audio Programmer (Joshua Hodge) – https://youtu.be/7n16Yw51xkI. These cover most relevant aspects of JUCE in detail. Much better than the tutorials on the JUCE website.
Here is the class example from week 7. It was pointed out to me that JUCE has deprecated the existing createAndAddParameter method (with all the arguments). The valid way to use this method is to create a RangedParameter and hand it to createAndAddParameter. Here is gain from the class example: I create an AudioParameterFloat which gets added to the value tree (std::make_unique is a safer substitute for new).
parameters.createAndAddParameter(std::make_unique<AudioParameterFloat>("gain", // parameter ID "gain", // parameter name NormalisableRange<float> (-60.0f, 12.0f), // range 0.0f, // default value "dB"));
Here is a collection of filter external source code for Pure Data.
Lowpass, highpass and phasors can be created by modifying the 1st order allpass. Bandpass and bandstop filters can be created by modifying the 2nd order allpass.
Download this zip archive for the source code: fulters.zip
source for softclip~. this shows how to pass the object structure “x” into the audio perform function.
#include "m_pd.h" #include #include #ifdef NT #pragma warning( disable : 4244 ) #pragma warning( disable : 4305 ) #endif /* ------------------------ softclip~ ----------------------------- */ /* tilde object to do a simple soft clipping algorithm. */ static t_class *softclip_class; typedef struct _softclip { t_object x_obj; /* obligatory header */ t_float gain; /* floats on the first inlet set the gain */ t_float sample_rate; t_float freq; } t_softclip; /* this is the actual performance routine which acts on the samples. It's called with a single pointer "w" which is our location in the DSP call list. We return a new "w" which will point to the next item after us. Meanwhile, w[0] is just a pointer to dsp-perform itself (no use to us), w[1] and w[2] are the input and output vector locations, and w[3] is the number of points to calculate. */ static t_int *softclip_perform(t_int *w) { t_softclip *x = (t_softclip *)(w[1]); t_float *in = (t_float *)(w[2]); t_float *out = (t_float *)(w[3]); int n = (int)(w[4]); // i like counting from zero, so i use samplenumber to count the offset from // the start of the in and out blocks int samplenumber = 0; float a = -0.5f; float b = 0.0; float c = 1.5f; float d = 0.0; float ingain; while (n--) { ingain = x->gain * *(in+samplenumber); if(ingain > 1) *(out+samplenumber) = 1.0f; else if(ingain < -1) *(out+samplenumber) = -1.0f; else *(out+samplenumber) = a * ingain * ingain * ingain + b * ingain * ingain + c * ingain + d; samplenumber++; } return (w+5); } /* called to start DSP. Here we call Pd back to add our perform routine to a linear callback list which Pd in turn calls to grind out the samples. */ static void softclip_dsp(t_softclip *x, t_signal **sp) { x->sample_rate = sp[0]->s_sr; dsp_add(softclip_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); } static void *softclip_new(void) { t_softclip *x = (t_softclip *)pd_new(softclip_class); x->freq = 432.0f; outlet_new(&x->x_obj, gensym("signal")); return (x); } /* this routine, which must have exactly this name (with the "~" replaced by "_tilde) is called when the code is first loaded, and tells Pd how to build the "class". */ void softclip_tilde_setup(void) { softclip_class = class_new(gensym("softclip~"), (t_newmethod)softclip_new, 0, sizeof(t_softclip), 0, A_DEFFLOAT, 0); /* this is magic to declare that the leftmost, "main" inlet takes signals; other signal inlets are done differently... */ /* also installs gain as the leftmost inlet float */ CLASS_MAINSIGNALIN(softclip_class, t_softclip, gain); /* here we tell Pd about the "dsp" method, which is called back when DSP is turned on. */ class_addmethod(softclip_class, (t_method)softclip_dsp, gensym("dsp"), (t_atomtype)0); }
source for rms~. this shows how to allocate and deallocate an array
#include "m_pd.h" #include#include #ifdef NT #pragma warning( disable : 4244 ) #pragma warning( disable : 4305 ) #endif /* ------------------------ rms~ ----------------------------- */ /* tilde object to take the absolute value of a signal at audio rate */ /* an argument for window size may be entered upon instantiation */ static t_class *rms_class; typedef struct _rms { t_object x_obj; // obligatory variable t_float x_f; // stores message recieved on audio inlet t_int window; // window size set by object argument t_float sum; // running sum of squared values t_float *squaretab; // table to hold squared values t_int tabread1; // pointer offset for newest table value t_int tabread2; // pointer offset for oldest table value } t_rms; static t_int *rms_perform(t_int *w) { t_rms *x = (t_rms *)(w[1]); t_float *in = (t_float *)(w[2]); t_float *out = (t_float *)(w[3]); int n = (int)(w[4]); // number of samples passed int blocksize = n; // sample from 0 up int sample = 0; // mean of squared value float mean = 0; // used to multiply instead of divide float oneOverWindow = 1.0f/x->window; while (n--) { // store newest squared value in the table *(x->squaretab+x->tabread1) = *(in+sample) * *(in+sample); // add this value to the running sum x->sum += *(x->squaretab+x->tabread1); // subtract the oldest value in the table x->sum -= *(x->squaretab+x->tabread2); // compute the current mean value mean = x->sum * oneOverWindow; // take the square root and send the value to outlet *(out+sample) = sqrt(mean); // increment to next sample sample++; // increment to next table position x->tabread1++; x->tabread2++; // reset to beginning of table if(x->tabread1 >= x->window) x->tabread1 = 0; // increment to next table position // reset to beginning of table if(x->tabread2 >= x->window) x->tabread2 = 0; } return (w+5); } static void rms_dsp(t_rms *x, t_signal **sp) { dsp_add(rms_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); } // setting window size void rms_windowset(t_rms *x, t_floatarg f) { // using a good value if( f >= 256 && f <= 4096 ) x->window = f; // blocking bad values else if( f < 256 ) { x->window = 256; post("rms~: window size must be set to a value between 256 and 4096"); } else if( f > 4096 ) { x->window = 4096; post("rms~: window size must be set to a value between 256 and 4096"); } } static void *rms_new(t_floatarg f) { int i; t_rms *x = (t_rms *)pd_new(rms_class); outlet_new(&x->x_obj, gensym("signal")); x->squaretab = 0; x->x_f = 0; // initialize the sum x->sum = 0; // changing windowsize on creation rms_windowset(x, f); // intializing table for largest window size x->squaretab = (t_float *)malloc(4097 * sizeof(t_float)); // initialzing the table to 0 for( i = 0; i < x->window; i++) *(x->squaretab+i) = 0; // initializing newest sample x->tabread1 = 0; // initializing oldest sample x->tabread2 = 1; return (x); } // function to clear the allocated square table static void rms_free(t_rms *x) { free(x->squaretab); if(x->squaretab != 0) { free(x->squaretab); x->squaretab = 0; } } void rms_tilde_setup(void) { rms_class = class_new(gensym("rms~"), (t_newmethod)rms_new, (t_method)rms_free, sizeof(t_rms), 0, A_DEFFLOAT, 0); CLASS_MAINSIGNALIN(rms_class, t_rms, x_f); class_addmethod(rms_class, (t_method)rms_dsp, gensym("dsp"), (t_atomtype)0); }
1) make an external that does something useful to your music/art/research/etc.
2) external should have an output for resultant data
3) comments or documentation should describe the external
4) due 4th week
1) make a variation of obj1 that changes either the text or message necessary to post the text.
2) send the source code and external to me. send via google drive if you are unable to email it.
3) due week 3
sunangle.c – this external uses the date and time from the seconds external to show the height and azimuth position of the sun. also shows how to handle an input list (same as A_GIMME)
/* sunangle.c - find the angle and azimuth of the sun at any latitude and longitude */ #include "m_pd.h" #include#include #define lotsOplaces 100 // generic, big-enough string length #define PI 3.141592653589793 typedef struct _sunangle // defines our object's internal variables for each instance in a patch { t_object x_ob; t_outlet *p_outletH; t_outlet *p_outletA; float year, month, date, hour, min, sec; float latitude; // first input float longitude; // second input } t_sunangle; t_class *sunangle_class; // global pointer to the object class - so max can reference the object // these are prototypes for the methods that are defined below void sunangle_bang(t_sunangle *x); void sunangle_seconds(t_sunangle *x, t_symbol *selector, int argcount, t_atom *argvec); void sunangle_latitude(t_sunangle *x, float f); void sunangle_longitude(t_sunangle *x, float f); void *sunangle_new(float lati, float longi); // prototype functions double calcJD(float, float, double); double calcTimeJulianCent(double); double calcEquationOfTime(double); double radToDeg(double); double degToRad(double); double calcGeomMeanLongSun(double); double calcGeomMeanAnomalySun(double); double calcSunDeclination(double); double calcObliquityCorrection(double); double calcMeanObliquityOfEcliptic(double); double calcSunApparentLong(double); double calcSunTrueLong(double); double calcGeomMeanLongSun(double); double calcSunEqOfCenter(double); double calcEccentricityEarthOrbit(double); void calcHA(float,float,float, float, float, float, float, float, float *, float *); //-------------------------------------------------------------------------- void sunangle_bang(t_sunangle *x) // x = reference to this instance of the object { float H, A; calcHA(x->latitude, x->longitude, x->year, x->month, x->date, x->hour, x->min, x->sec, &H, &A); outlet_float(x->p_outletH, H); outlet_float(x->p_outletA, A); } void sunangle_seconds(t_sunangle *x, t_symbol *selector, int argcount, t_atom *argvec) { if(argcount != 6) post("input to sunangle should be \"year month day hour minute second\""); else { if(argvec[0].a_type == A_FLOAT) x->year = argvec[0].a_w.w_float; else { post("the first item in list should be a float"); return; } if(argvec[1].a_type == A_FLOAT) x->month = argvec[1].a_w.w_float; else { post("the second item in list should be a float"); return; } if(argvec[2].a_type == A_FLOAT) x->date = argvec[2].a_w.w_float; else { post("the third item in list should be a float"); return; } if(argvec[3].a_type == A_FLOAT) x->hour = argvec[3].a_w.w_float; else { post("the fourth item in list should be a float"); return; } if(argvec[4].a_type == A_FLOAT) x->min = argvec[4].a_w.w_float; else { post("the fifth item in list should be a float"); return; } if(argvec[5].a_type == A_FLOAT) x->sec = argvec[5].a_w.w_float; else { post("the sixth item in list should be a float"); return; } } sunangle_bang(x); } void sunangle_latitude(t_sunangle *x, float f) { x->latitude = f; } // this gets called when something goes into inlet 2 void sunangle_longitude(t_sunangle *x, float f) { x->longitude = f; } //-------------------------------------------------------------------------- void *sunangle_new(float lati, float longi) // n = int argument typed into object box (A_DEFLONG) -- defaults to 0 if no args are typed { t_sunangle *x; // local variable (pointer to a t_sunangle data structure) x = (t_sunangle *)pd_new(sunangle_class); // create a new instance of this object // add inputs and outputs x->p_outletH = outlet_new(&x->x_ob, gensym("float")); x->p_outletA = outlet_new(&x->x_ob, gensym("float")); x->latitude = lati; // set initial (default) left operand value in the instance's data structure x->longitude = longi; // set initial (default) right operand value (n = variable passed to sunangle_new) return(x); // return a reference to the object instance } //-------------------------------------------------------------------------- void sunangle_setup(void) { sunangle_class = class_new(gensym("sunangle"), (t_newmethod)sunangle_new, 0, sizeof(t_sunangle), 0, A_DEFFLOAT, A_DEFFLOAT, 0); // class_new() loads our external into pd's memory so it can be used in a patch // sunangle_new = object creation method defined above class_addbang(sunangle_class, (t_method)sunangle_bang); class_addlist(sunangle_class, (t_method)sunangle_seconds); class_addmethod(sunangle_class, (t_method)sunangle_latitude, gensym("latitude"), A_FLOAT, 0); class_addmethod(sunangle_class, (t_method)sunangle_longitude, gensym("longitude"), A_FLOAT, 0); } // below this line is the actual sun position code //-------------------------------------------------------------------------- void calcHA(float lat, float lon, float year, float month, float date, float hour, float min, float sec, float *H, float *A){// sec is type double for calculations // declare calculated things double day, JD, T, minTime, time_offset, tst, sha, theta, cosPhi, exoatmElevation, refractionCorrection, te, azimuth; double zenith; double azDenom; int TZ = 0, DS = 0; // calculate astronomical times day = date+(hour+(min+sec/60.)/60.)/24.; JD = calcJD(year,month,day); T = calcTimeJulianCent(JD); minTime = calcEquationOfTime(T); time_offset = minTime-4*(-lon)+60*(TZ-DS); tst = hour*60+min+sec/60+time_offset; sha = tst/4-180; if (sha < -180) sha += 360.0; theta = calcSunDeclination(T); cosPhi = sin(degToRad(lat))*sin(degToRad(theta))+cos(degToRad(lat))* cos(degToRad(theta))*cos(degToRad(sha)); // exoatmospheric elevation angle exoatmElevation = 90 - radToDeg(acos(cosPhi)); // rudimentary refraction correction if (exoatmElevation > 85.0) { refractionCorrection = 0.0; } else { te = tan(degToRad(exoatmElevation)); if (exoatmElevation > 5.0) { refractionCorrection = 58.1 / te - 0.07 / (te*te*te) + 0.000086 / (te*te*te*te*te); } else if (exoatmElevation > -0.575) { refractionCorrection = 1735.0 + exoatmElevation * (-518.2 + exoatmElevation * (103.4 + exoatmElevation * (-12.79 + exoatmElevation * 0.711) ) ); } else { refractionCorrection = -20.774 / te; } refractionCorrection = refractionCorrection / 3600.0; } // refraction-corrected elevation angle *H = exoatmElevation + refractionCorrection; // work on azimuthal concerns if (cosPhi > 1.0) { cosPhi = 1.0; } else if (cosPhi < -1.0) { cosPhi = -1.0; } zenith = radToDeg(acos(cosPhi)); azDenom = cos(degToRad(lat))*sin(degToRad(zenith)); if (fabs(azDenom) > 0.001) { double azRad = (( sin(degToRad(lat)) * cos(degToRad(zenith)) ) - sin(degToRad(theta))) / azDenom; if (fabs(azRad) > 1.0) { if (azRad < 0) { azRad = -1.0; } else { azRad = 1.0; } } azimuth = 180.0 - radToDeg(acos(azRad)); if (sha > 0.0) { azimuth = -azimuth; } } else { if (lat > 0.0) { azimuth = 180.0; } else { azimuth = 0.0; } } if (azimuth < 0.0) { azimuth += 360.0; } *A = azimuth; } double calcJD(float year, float month, double day){ double JD, A, B; if(month<=2) { year = year-1; month = month+12; } A = floor((double)(year)/100.0); // note cast of year B = 2 - A + floor(A/4); JD = floor(365.25*(year + 4716.0)) + floor(30.6001*(month+1)) + day + B - 1524.5; return JD; } double calcTimeJulianCent(double jd){ double T; T = (jd - 2451545.0)/36525.0; return T; } double calcEquationOfTime(double t){ double minTime, epsilon, l0, e, m, y, sin2l0, sinm, cos2l0, sin4l0; double sin2m, Etime; epsilon = calcObliquityCorrection(t); l0 = calcGeomMeanLongSun(t); e = calcEccentricityEarthOrbit(t); m = calcGeomMeanAnomalySun(t); y = tan(degToRad(epsilon)/2.0); y = y*y; sin2l0 = sin(2.0 * degToRad(l0)); sinm = sin(degToRad(m)); cos2l0 = cos(2.0 * degToRad(l0)); sin4l0 = sin(4.0 * degToRad(l0)); sin2m = sin(2.0 * degToRad(m)); Etime = y * sin2l0 - 2.0 * e * sinm + 4.0 * e * y * sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m; minTime = radToDeg(Etime)*4.0; return minTime; } double radToDeg(double angleRad){ return (180.0 * angleRad / PI); } double degToRad(double angleDeg){ return (PI * angleDeg / 180.0); } double calcGeomMeanLongSun(double t){ double L0 = 280.46646 + t * (36000.76983 + 0.0003032 * t); while(L0 > 360.0) { L0 -= 360.0; } while(L0 < 0.0) { L0 += 360.0; } return L0; // in degrees } double calcEccentricityEarthOrbit(double t){ double e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t); return e; // unitless } double calcGeomMeanAnomalySun(double t){ double M = 357.52911 + t * (35999.05029 - 0.0001537 * t); return M; // in degrees } double calcObliquityCorrection(double t){ double e0 = calcMeanObliquityOfEcliptic(t); double omega = 125.04 - 1934.136 * t; double e = e0 + 0.00256 * cos(degToRad(omega)); return e; // in degrees } double calcSunDeclination(double t){ double e = calcObliquityCorrection(t); double lambda = calcSunApparentLong(t); double sint = sin(degToRad(e)) * sin(degToRad(lambda)); double theta = radToDeg(asin(sint)); return theta; // in degrees } double calcMeanObliquityOfEcliptic(double t){ double seconds = 21.448 - t*(46.8150 + t*(0.00059 - t*(0.001813))); double e0 = 23.0 + (26.0 + (seconds/60.0))/60.0; return e0; // in degrees } double calcSunApparentLong(double t){ double o = calcSunTrueLong(t); double omega = 125.04 - 1934.136 * t; double lambda = o - 0.00569 - 0.00478 * sin(degToRad(omega)); return lambda; // in degrees } double calcSunTrueLong(double t){ double l0 = calcGeomMeanLongSun(t); double c = calcSunEqOfCenter(t); double O = l0 + c; return O; // in degrees } double calcSunEqOfCenter(double t){ double m = calcGeomMeanAnomalySun(t); double mrad = degToRad(m); double sinm = sin(mrad); double sin2m = sin(mrad+mrad); double sin3m = sin(mrad+mrad+mrad); double C = sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) + sin2m * (0.019993 - 0.000101 * t) + sin3m * 0.000289; return C; // in degrees }
Here is seconds.c, which uses <time.h> functions to send the day and time out as a list (out of a single list outlet).
/* seconds.c - this object has 1 inlets and 6 outlets it responds the 'bang' message in the left inlet it responds to the 'assistance' message sent by Max when the mouse is positioned over an inlet or outlet (including an assistance method is optional, but strongly sugggested) */ #include <time.h> #include "m_pd.h" typedef struct _seconds // defines our object's internal variables for each instance in a patch { t_object x_ob; t_outlet *x_outlist; t_atom date[6]; } t_seconds; t_class *seconds_class; // global pointer to the object class - so max can reference the object // these are prototypes for the methods that are defined below void seconds_bang(t_seconds *x); void *seconds_new(void); void seconds_setup(void); //-------------------------------------------------------------------------- void seconds_setup(void) { seconds_class = class_new(gensym("seconds"), (t_newmethod)seconds_new, 0, sizeof(t_seconds), 0, 0); class_addbang(seconds_class, seconds_bang);// the method it uses when it gets a bang in the left inlet } //-------------------------------------------------------------------------- void seconds_bang(t_seconds *x) // x = reference to this instance of the object { time_t rawTime; float year, month, date, hour, min, sec; struct tm *now; time(&rawTime); now = gmtime(&rawTime); year = (now->tm_year + 1900); month = (now->tm_mon + 1); date = (now->tm_mday); hour = now->tm_hour; min = now->tm_min; sec = now->tm_sec; SETFLOAT(x->date+0, year); SETFLOAT(x->date+1, month); SETFLOAT(x->date+2, date); SETFLOAT(x->date+3, hour); SETFLOAT(x->date+4, min); SETFLOAT(x->date+5, sec); outlet_list(x->x_outlist, gensym("list"), 6, (x->date)); } //-------------------------------------------------------------------------- void *seconds_new(void) // n = int argument typed into object box (A_DEFLONG) -- defaults to 0 if no args are typed { t_seconds *x;// local variable (pointer to a t_seconds data structure x = (t_seconds *)pd_new(seconds_class); // create a new instance of this object x->x_outlist = outlet_new(&x->x_ob, gensym("list")); return(x); // return a reference to the object instance }