// // Copyright: Copyright 2003 Craig Stuart Sapp // Programmer: Craig Stuart Sapp // Creation Date: Fri Apr 11 22:30:23 PDT 2003 // Last Modified: Mon Apr 14 14:14:18 PDT 2003 // Filename: kola.c // Web Address: http://peabody.sapp.org/class/dmp2/lab/kola/kola.c // Syntax: C; Max4/MSP2 External Object; CodeWarrior 6.0 // OS: Mac OS 9; PPC // // Description: constant overlap-add looping buffer using a // Kaiser-Bessel window. // #include "ext.h" #include "z_dsp.h" #include typedef struct { t_pxobject msp_data; // must always be the first field; used by MSP float* window; // window data buffer float* windowb; // pointer to last half of buffer long winsize; // size of buffer for window float alpha; // alpha parameter for window float* buffer; // audio buffer long bufsize; // size of audio buffer long index; // index into audio buffer long recordQ; // recording state long loopQ; // looping state long endQ; // ending state long bufallocsize; // max size of audio buffer if resized } MyObject; void* object_data; // pointer to data for object (created in setup()); // function declarations: void main (void); void* create_object (float maxsize); void destroy_object (MyObject* mo); double BesselI0 (double x); void MakeKaiserWindow (float* window, int size, float alpha); void InputBang (MyObject* mo); void InputLoopTime (MyObject* mo, float value); void InputWindowSize (MyObject* mo, int value); void InputAlpha (MyObject* mo, float value); void MessageDSP (MyObject* mo, t_signal** signal, short* count); void MessageStart (MyObject* mo); void MessageStop (MyObject* mo); void MessageClear (MyObject* mo); t_int* Perform (t_int *parameters); ////////////////////////////////////////////////////////////////////////// // // Initialization functions: // ////////////////////////////// // // main -- called once when object is created in a Max/MSP patch window. // void main(void) { setup((t_messlist**)&object_data, (method)create_object, (method)destroy_object, (short)sizeof(MyObject), NULL, A_FLOAT, A_NOTHING); addftx ((method)InputAlpha, 1); // input 4: window alpha parameter addinx ((method)InputWindowSize, 2); // input 3: half size of window addftx ((method)InputLoopTime, 3); // input 2: loop time (milliseconds) addbang((method)InputBang); addmess((method)MessageDSP, "dsp", A_CANT, A_NOTHING); addmess((method)MessageClear, "clear", A_NOTHING); addmess((method)MessageStart, "start", A_NOTHING); addmess((method)MessageStop, "stop", A_NOTHING); dsp_initclass(); } ////////////////////////////// // // create_object -- object initializing function. // void* create_object(float maxsize) { long maxsamples; MyObject *mo = (MyObject*)newobject(object_data); // create data storage dsp_setup((t_pxobject*)mo, 1); // audio input 1 outlet_new((t_pxobject*)mo, "signal"); // audio output 1 floatin(mo, 1); // input 4: alpha intin (mo, 2); // input 3: windw 1/2 size floatin(mo, 3); // input 2: loop time (ms) if (maxsize < 1) { post("Error: buffer size too small, setting it to 1000 milliseconds."); maxsize = 1000.0; } maxsamples = (long)(maxsize/1000.0 * 44100.0 + 0.5); // 44.1 kHz srate mo->buffer = (float*)malloc(maxsamples * sizeof(float)); mo->bufallocsize = maxsamples; mo->bufsize = maxsamples; mo->alpha = 1.25; mo->window = NULL; InputWindowSize(mo, 1024); MessageClear(mo); return mo; // return pointer to data storage } ////////////////////////////// // // destroy_object -- what to do when removing an object from a patch window. // void destroy_object(MyObject* mo) { dsp_free(&(mo->msp_data)); free(mo->buffer); free(mo->window); mo->buffer = NULL; mo->window = NULL; mo->windowb = NULL; mo->bufallocsize = 0; mo->bufsize = 0; } ////////////////////////////////////////////////////////////////////////// // // Behavior functions: // ////////////////////////////// // // InputBang -- turn the looping on/off. // void InputBang(MyObject* mo) { if (mo->loopQ) { MessageStop(mo); } else { MessageStart(mo); } } ////////////////////////////// // // MessageStart -- start the looping/recording. // void MessageStart(MyObject* mo) { mo->loopQ = 1; mo->recordQ = 1; mo->index = 0; post("kola~ start"); } ////////////////////////////// // // MessageStop -- stop the looping/recording. // void MessageStop(MyObject* mo) { mo->endQ = 1; post("kola~ stop"); } ////////////////////////////// // // MessageClear -- set the audio buffer to zero. // void MessageClear(MyObject* mo) { int i; for (i=0; ibufallocsize; i++) { mo->buffer[i] = 0.0; } mo->index = 0; } ////////////////////////////// // // InputAlpha -- input the alpha parameter for the Kaiser-Bessel // window. Use alpha=0 for a triangle window, alpha=~1.25 for a // raised cosine (hanning) window, and alpha>=10 for an approximation // to a square window. // void InputAlpha(MyObject* mo, float value) { MakeKaiserWindow(mo->window, mo->winsize, value); mo->alpha = value; } ////////////////////////////// // // InputWindowSize -- input the half-window size. // void InputWindowSize (MyObject* mo, int value) { if (value < 2) { value = 2; } value = value + (2 - value%2); // force value to be even value = value * 2; // calculate the entire window size if (mo->window != NULL) { free(mo->window); } mo->window = (float*)malloc(value * sizeof(float)); mo->winsize = value; mo->windowb = mo->window + value/2; // pointer to last half of window InputAlpha(mo, mo->alpha); // fill the new window with data } ////////////////////////////// // // InputLoopTime -- input the loop time in milliseconds // void InputLoopTime(MyObject* mo, float value) { long samples = (long)(value / 1000.0 * 44100 + 0.5); if (samples >= mo->bufallocsize) { samples = mo->bufallocsize; post("kola~: cannot set loop buffer that big!"); } if (samples < 4) { samples = 4; post("kola~: setting minimum audio delay buffer sample size to 4."); } mo->bufsize = samples; mo->index = 0; } ////////////////////////////// // // MessageDSP -- What to do when a "dsp" message arrives into the object. // void MessageDSP(MyObject* mo, t_signal** signal, short* count) { #pragma unused(count) int paramcount = 5; dsp_add(Perform, paramcount, paramcount, mo, signal[0]->s_vec, signal[1]->s_vec, signal[0]->s_n); } ////////////////////////////// // // Perform -- simple implementation, looping must be stopped before // starting another loop; otherwise, you will get clicks. // To do: when the loop is turned off, window from the immediate // location, but do not do this if inside of the overlap region. // t_int* Perform(t_int *parameters) { long paramcount = (long) (parameters[1]); MyObject *mo = (MyObject*)(parameters[2]); t_float *input = (t_float*) (parameters[3]); t_float *output = (t_float*) (parameters[4]); long count = (long) (parameters[5]); long i; long tindex; // temporary index for (i=0; irecordQ) { mo->buffer[mo->index] = input[i]; if (mo->index < mo->winsize/2) { output[i] = mo->buffer[mo->index] * mo->window[mo->index]; } else if (mo->index >= mo->bufsize - mo->winsize/2) { tindex = mo->winsize/2 - (mo->bufsize - mo->index); output[i] = mo->buffer[mo->index] * mo->windowb[tindex] + mo->buffer[tindex] * mo->window[tindex]; } else { output[i] = mo->buffer[mo->index]; } } else if (mo->loopQ) { if (mo->index < mo->winsize/2) { tindex = mo->bufsize - mo->winsize/2 + mo->index; output[i] = mo->buffer[mo->index] * mo->window[mo->index] + mo->buffer[tindex] * mo->windowb[mo->index]; } else if (mo->index >= mo->bufsize - mo->winsize/2) { if (mo->index == mo->bufsize - mo->winsize/2) { post("kola~ ending"); } tindex = mo->winsize/2 - (mo->bufsize - mo->index); output[i] = mo->buffer[mo->index] * mo->windowb[tindex]; } else { output[i] = mo->buffer[mo->index]; } } else { output[i] = 0.0; } mo->index++; if (mo->recordQ && (mo->index >= mo->bufsize)) { mo->index = mo->winsize/2; mo->recordQ = 0; } else if ((!mo->recordQ) && (!mo->endQ) && mo->loopQ && (mo->index >= mo->bufsize - mo->winsize/2)) { mo->index = 0; } else if (mo->index >= mo->bufsize) { mo->endQ = 0; mo->loopQ = 0; mo->recordQ = 0; mo->index = 0; } } return parameters+paramcount+1; // return pointer to next item in DSP chain } ////////////////////////////////////////////////////////////////////////// // // helper functinons // ////////////////////////////// // // MakeKaiserWindow -- Kaiser Bessel Window // fills the input window array with size samples of the // Kaiser window with the given tuning parameter alpha. // #ifndef PI #define PI 3.14159265358979323846264338328 #endif void MakeKaiserWindow(float* window, int size, float alpha) { double sumvalue = 0.0; int i; for (i=0; i