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