// // Programmer: Asha Srinivasan // Programmer: Craig Stuart Sapp // Creation Date: Fri Feb 28 18:41:42 PST 2003 // Last Modified: Mon Mar 31 16:54:09 EST 2003 // Filename: polyecho.c // Syntax: C; Max4/MSP2 External Object; CodeWarrior 6.0 // OS: Mac OS 9; PPC // // Description: polyphonic note echoer with one echo allowed per note. // #include "ext.h" typedef struct { // local echo data: void* clock; // clock data structure pointer float outvel; // current output velocity long state; // 0=off 1=on long inkey; // store key in // global MyObject data: float *decay; // decay of amplitude long *min; // minimum velocity before an echo is killed long *duration; // duration between output bangs long *gap; // duration between note-offs and the next note-on. void** outputKeyNumberLocal; void** outputVelocityLocal; } echodata; typedef struct { t_object max_data; // must always be the first field; used by Max echodata data[128]; // echo states for each key number float tempvel; // hold velocity until the key number is known long min; // minimum velocity before an echo is killed long duration; // duration between output bangs float decay; // decay amount between attacks long gap; // duration between note-offs and the next note-on void* outputKeyNumber; void* outputVelocity; } MyObject; void* object_data; // pointer to data for object (created in setup()); // function declarations: void main (void); void* create_object (long defaultDuration); void destroy_object (MyObject *mo); void InputKeyNumber (MyObject* mo, long value); void InputVelocity (MyObject* mo, int value); void InputDecay (MyObject* mo, float value); void InputDuration (MyObject* mo, long value); void ClockAction (echodata *ed); void MessageStop (MyObject *mo); void MessageStopNote (echodata *ed); void MessageStartNote(echodata *ed); long midilimit (long value); ////////////////////////////////////////////////////////////////////////// // // Initialization functions: // void main(void) { setup((t_messlist**)&object_data, (method)create_object, (method)destroy_object, (short)sizeof(MyObject), NULL, A_DEFLONG, A_NOTHING); addint((method)InputKeyNumber); addftx((method)InputDecay, 1); addinx((method)InputDuration, 2); addinx((method)InputVelocity, 3); } ////////////////////////////// // // create_object -- // void* create_object(long defaultInterval) { MyObject *mo = (MyObject*)newobject(object_data); int i; if (defaultInterval < 20) { defaultInterval = 100; } for (i=0; i<128; i++) { mo->data[i].clock = clock_new(&(mo->data[i]), (method)ClockAction); mo->data[i].outvel = 0; mo->data[i].state = 0; mo->data[i].inkey = i; mo->data[i].decay = &(mo->decay); mo->data[i].min = &(mo->min); mo->data[i].duration = &(mo->duration); mo->data[i].gap = &(mo->gap); mo->data[i].outputKeyNumberLocal = &(mo->outputKeyNumber); mo->data[i].outputVelocityLocal = &(mo->outputVelocity); } mo->min = 5; mo->duration = defaultInterval; mo->gap = 100; mo->tempvel = 0; mo->decay = 0.5; mo->outputVelocity = intout(mo); //outlet 2 mo->outputKeyNumber = intout(mo); //outlet 1 floatin(mo, 1); // inlet 4: decay intin(mo, 2); // inlet 3: duration intin(mo, 3); // inlet 2: velocity return mo; } ////////////////////////////// // // destroy_object -- what to do when the object is deleted from the patch. // In this case, you must free the clock object. // void destroy_object(MyObject *mo) { int i; for (i=0; i<128; i++) { freeobject((t_object*)mo->data[i].clock); } } ////////////////////////////////////////////////////////////////////////// // // Behavior functions: // ////////////////////////////// // // InputKeyNumber -- // void InputKeyNumber(MyObject* mo, long value) { value = midilimit(value); if (mo->tempvel == 0) return; MessageStopNote(&(mo->data[value])); mo->data[value].inkey = value; //when you get a key number, store it mo->data[value].outvel = mo->tempvel; MessageStartNote(&(mo->data[value])); } ////////////////////////////// // // InputVelocity -- store the input attack velocity. // void InputVelocity(MyObject* mo, int value) { mo->tempvel = (float)value; } ////////////////////////////// // // InputDuration -- store the input duration value. // void InputDuration(MyObject* mo, long value) { if (value < 1) { value = 1; } mo->duration = value; } ////////////////////////////// // // InputDecay -- store the input decay value. // void InputDecay(MyObject* mo, float value) { mo->decay = value; } ////////////////////////////// // // ClockAction -- what to do when the event scheduler is ready. // void ClockAction(echodata *ed) { if (ed->state == 1) { clock_delay(ed->clock, *(ed->gap)); outlet_int(*(ed->outputVelocityLocal), 0); outlet_int(*(ed->outputKeyNumberLocal), ed->inkey); ed->state = 0; ed->outvel = *(ed->decay) * ed->outvel; //subsequent note decreases if (ed->outvel < *(ed->min)) { MessageStopNote(ed); } } else { clock_delay(ed->clock, *(ed->duration)); outlet_int(*(ed->outputVelocityLocal), ed->outvel); outlet_int(*(ed->outputKeyNumberLocal), ed->inkey); ed->state = 1; } } ////////////////////////////// // // MessageStop -- stop all echos on all keys. // void MessageStop(MyObject *mo) { int i; for (i=0; i<128; i++) { MessageStopNote(&(mo->data[i])); } } ////////////////////////////// // // MessageStopNote -- stop the echo of a particular key number. // void MessageStopNote(echodata *ed) { clock_unset(ed->clock); outlet_int(*(ed->outputVelocityLocal), 0); outlet_int(*(ed->outputKeyNumberLocal), ed->inkey); ed->state = 0; } ////////////////////////////// // // midilimit -- prevent a value going out of the range from 0 to 127. // void MessageStartNote(echodata *ed) { clock_delay(ed->clock, 0); } ////////////////////////////// // // midilimit -- prevent a value going out of the range from 0 to 127. // long midilimit(long value) { if (value < 0) { return 0; } else if (value > 127) { return 127; } else { return value; } }