MUS 177/206 – two simple audio externals: softclip~ and rms~

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);
}

MUS177/206 – sunangle.c

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
}

MUS177/206 – seconds.c

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 
}

MUS177/206 – window makefile changes for Pd externals

The makefile to compile the example externals from Pd 0.49 needs to be edited in order to work with Visual Studio Community 2017.

Do all of your editing and compilation on a copy of Pd in your \Users folder

The VC definition needs to be changed (as usual) to point to the 64-bit tools in VSC. Your setting will likely be different than mine.

VC="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.15.26726"

The PDNTCFLAGS need to be changed to eliminate /WX (treat all warnings as errors, and add a definition for PD_LONGINTTYPE to give PD 64-bit pointers. Without this, the audio example (dspobj~) will crash.

PDNTCFLAGS = /W3 /DNT /DPD /DPD_LONGINTTYPE="long long" /nologo

Now, make sure PDNTLDIR points to the 64 bit library folder by appending “\x64″ to the path

PDNTLDIR = $(VC)\lib\x64

Finally, remove references to old names.lib and kernel32.lib and change libc.lib to libcmt.lib

PDNTLIB = $(PDNTLDIR)\libcmt.lib \
..\..\bin\pd.lib

music 177/206 syllabus – fall 2018

music 177/206 – custom programming for music – fall 2018

instructor – tom erbe – tre@music.ucsd.edu

•••••focus

this year we will be learning to program a pd external and a JUCE compatible plugin. to do this we will cover 4 topics.

  1. c/c++ programming
  2. the pd SDK
  3. the JUCE SDK
  4. computer music algorithms

••••schedule

  1. basics (basics and compilers – setting up a dev environment)
  2. pd 1 – making externals and c rehash
  3. pd 2 – adding dsp (first assignment)
  4. dsp code 1 – amplifiers delay and oscillators
  5. dsp code 2 – filters and ffts (second assignment)
  6. JUCE 1 – first examples
  7. JUCE 2 – more examples and (final project assignment)
  8. JUCE 3 (integrating dsp)
  9. guis
  10. other topics

••••texts – software

these books are very helpful – especially if you feel the need to catch up.

  • C Primer Plus by Stephen Prata
  • Computer Music by Charles Dodge
  • DAFX by Udo Zolzer
  • JUCE – download the SDK from http://www.juce.com
  • pd – download the latest pd from http://crca.ucsd.edu/~msp

you will also need a decent compiler. this is xcode for macintosh or visual studio 2018  for windows.

 

••••• outline – likely topics

  1. overview of PD – externals – streaming/control/objects/messages in typical audio software
  2. look over simple PD examples – see what we understand
  3. c++ – structure of an object
  4. c++ – memory allocation – structures, arrays and pointers
  5. signal processing basics – simple amplitude modulation
  6. signal processing – lfo – random generators
  7. signal processing – distortion
  8. signal processing – compression expansion gating
  9. signal processing – simple filtering
  10. signal processing – oscillators
  11. the JUCE object – process
  12. the JUCE object – setparameter, getparameter
  13. the JUCE object – GUI code
  14. using visual c++
  15. using the debugger
  16. using xcode

•••••my office hours

1-3 tuesday and thursday

pm or email me for other hours

tre@music.ucsd.edu

tre@soundhack.com

•••••class requirements

3 projects of increasing complexity…. one is a final project. 206 students will be required to use their final project in a piece or for research.

****class notes & assignments

week 1 – windows makefile changes for Pd externals

week 1 – assignment 1

week 2 – seconds.c external, showing how to output a list

week 2 – sunangle.c external, calculating the position of the sun

week 2 – assignment 2

week 2 – audio external examples: softclip~ and rms~

MUS206: Hardware Synthesis Syllabus

Hardware Synthesis Syllabus

Topics for Fall 2018

  1. A) Patching and Signals: Control Voltage, Audio, AC, DC, Gates, Triggers (Strange: Chapter 5); B) Slope, Envelope and LFO (Maths Manual – Make Noise Website)
  2. Oscillators: Sine, Square, Triangle and Ramp; Sync; Linear & Log Frequency; Vibrato & Gliss; Subharmonics; Additive; Noise; Multiple Oscillators; ASR (Strange: Chapter 3)
  3. Amplifiers: Tremolo; Envelope and LFO; Amplitude and Ring Modulation, Envelope Tracking, Types of VCAs (Strange: Chapter 4)
  4. Modulation: More Amplitude and Ring Mod, Wave Folding, Frequency Modulation, Chaotic Oscillation, Feedback Networks (Strange: Chapter 7-8)
  5. Playing 1st pieces.
  6. Sequencing and Variation, Structure, Rhythmic Patching, Clocking (Rene Manual – Make Noise Website)
  7. Holiday
  8. Musique Concrete Techniques. Computer Interface: MIDI, Control Voltage, Gates; Recording Material
  9. Reverb and Echo; External Processing; Processing Computer Signals
  10. Playing 2nd pieces

Readings from Allan Strange: Electronic Music; Make Noise Website

Listening Assignments

Pieces Presented on Week 5 and Week 10

MUS271A (Max w6) – delay & filtering

This lesson will cover two fundamental methods in processing sound: delay and filtering. These techniques are related as filtering is based on a mixing a delayed sound with itself. Like all techniques covered in this class, this is only an introduction, there are many more ideas that can be developed beyond the patches presented here. The patches for this lesson can be downloaded here:

 


delay

Screen Shot 2018-07-27 at 9.24.41 AMDelay is a method of transmitting sound through a media so that it can be heard later. This media can be magnetic tape, digital memory, or even the air.
In Max this is done with two objects, tapin~ and tapout~. The audio memory is defined by tapin~, and tapout~ needs to be connected to tapin~ to have it use the tapin~ memory. The argument to tapin~ defines the maximum length of memory, in this patch that is 1000 ms. Both the argument and the input parameter to tapout~ is the delay time. In the case of this patch we are listening to the sound that entered the audio memory 395 ms ago. The tapout~ delay time will be limited to the length of audio memory defined. Any number of tapout~ objects can be connected to a single tapin~ object.


 

Screen Shot 2018-07-27 at 9.47.59 AMWith many natural acoustic spaces (e.g. in a large room), the delayed sound re-enters the delay after it is heard, and the echo is repeated many times. This repeat can be easily added by sending some of the sound from tapout~ back into tapin~. In this patch, the output is multiplied by a feedback gain of 0.5 and added to the input signal before entering the delay line. This gain represents the gain reduction after each echo. If you select the first preset, you will here multiple decaying echoes. The second preset has much less gain reduction, and is an example of a rhythmic use of delay. The third preset shows the effect of having gain at or above 1.0. The signal soon clips and the delay memory soon fills with distortion. The clip~ object is essential to prevent the volume from getting out of control.


 

Screen Shot 2018-07-27 at 11.54.52 AMThis next patch shows the simple addition of a sine oscillator as a modulation source for the delay time. When time is modulated, the period of any pitched material in the delay line is expanded and contracted. This will cause doppler pitch shifting effects. The 4 presets show various types of time modulation. The fourth example uses an audio frequency modulation oscillator. This will frequency modulate the contents of the delay memory. Flange, chorus and pitch shifting are all created using carefully tuned time modulation. Try to expand on this patch to create these effects. It will help to use sound sources other than the percussive “pingmaker” subpatch to demonstrate these effects.


 

to be continued….