// // Copyright: Copyright 2003 Craig Stuart Sapp // Programmer: Craig Stuart Sapp // Creation Date: Sun Mar 9 13:24:23 PST 2003 // Last Modified: Sun Mar 9 17:24:27 PST 2003 // Filename: polymetro.c // Web Address: http://peabody.sapp.org/class/dmp2/lab/polymetro/polymetro.c // Syntax: C; Max4/MSP2 External Object; CodeWarrior 6.0 // OS: Mac OS 9; PPC // // Description: A Polyrhythmic metronome inspired by Andrew Winn // (www.andrewwinn.com) and Dominic Muldowney (domdido@aol.com). // Up to 6 polyrhythmic channels can be created at the // same time with this object. // #include "ext.h" #define MAXPOLY 6 /* maximum number of polyrhymic voices (cannot change) */ typedef struct { t_object max_data; // must always be the first field; used by Max float duration; // duration between output bangs void* clockStartCycle; // clock to start the cycle void* clock[MAXPOLY]; // clock data structure pointer int divisions[MAXPOLY]; // number of divisions for each channel int current[MAXPOLY]; // curr polyphonic position for each channel void* output[MAXPOLY]; // pointers to outlet, one for each channel } MyObject; void* object_data; // pointer to data for object (created in setup()); // function declarations: void main (void); void* create_object (void); void destroy_object (MyObject *mo); void InputBang (MyObject* mo); void InputInt (MyObject* mo, long value); void InputDuration (MyObject* mo, float value); void ClockStartCycle (MyObject* mo); void ClockActionA (MyObject *mo); void ClockActionB (MyObject *mo); void ClockActionC (MyObject *mo); void ClockActionD (MyObject *mo); void ClockActionE (MyObject *mo); void ClockActionF (MyObject *mo); void InputA (MyObject *mo, long value); void InputB (MyObject *mo, long value); void InputC (MyObject *mo, long value); void InputD (MyObject *mo, long value); void InputE (MyObject *mo, long value); void InputF (MyObject *mo, long value); void MessageStop (MyObject *mo); void MessageStart (MyObject *mo); void MessageReset (MyObject *mo); ////////////////////////////////////////////////////////////////////////// // // Initialization functions: // void main(void) { setup((t_messlist**)&object_data, (method)create_object, (method)destroy_object, (short)sizeof(MyObject), NULL, A_NOTHING); addbang((method)InputBang); addint((method)InputInt); addftx((method)InputDuration, 1); addinx((method)InputF, 2); addinx((method)InputE, 3); addinx((method)InputD, 4); addinx((method)InputC, 5); addinx((method)InputB, 6); addinx((method)InputA, 7); addmess((method)MessageStop, "stop", A_NOTHING); addmess((method)MessageStart, "start", A_NOTHING); addmess((method)MessageReset, "reset", A_NOTHING); } ////////////////////////////// // // create_object -- // void* create_object(void) { MyObject *mo = (MyObject*)newobject(object_data); mo->divisions[0] = 1; mo->divisions[1] = 0; mo->divisions[2] = 0; mo->divisions[3] = 0; mo->divisions[4] = 0; mo->divisions[5] = 0; mo->current[0] = 0; mo->current[1] = 0; mo->current[2] = 0; mo->current[3] = 0; mo->current[4] = 0; mo->current[5] = 0; mo->clock[0] = clock_new(mo, (method)ClockActionA); mo->clock[1] = clock_new(mo, (method)ClockActionB); mo->clock[2] = clock_new(mo, (method)ClockActionC); mo->clock[3] = clock_new(mo, (method)ClockActionD); mo->clock[4] = clock_new(mo, (method)ClockActionE); mo->clock[5] = clock_new(mo, (method)ClockActionF); mo->clockStartCycle = clock_new(mo, (method)ClockStartCycle); mo->duration = 250.0; // default duration of 250 milliseconds mo->output[5] = intout(mo); // outlet 6: sixth rhythm channel mo->output[4] = intout(mo); // outlet 5: fifth rhythm channel mo->output[3] = intout(mo); // outlet 4: fourth rhythm channel mo->output[2] = intout(mo); // outlet 3: third rhythm channel mo->output[1] = intout(mo); // outlet 2: second rhythm channel mo->output[0] = intout(mo); // outlet 1: first rhythm channel floatin(mo, 1); // inlet 8: duration between notes of first polyrhythm intin(mo, 2); // inlet 7: sixth polyrhythm value intin(mo, 3); // inlet 6: fifth polyrhythm value intin(mo, 4); // inlet 5: fourth polyrhythm value intin(mo, 5); // inlet 4: third polyrhythm value intin(mo, 6); // inlet 3: second polyrhythm value intin(mo, 7); // inlet 2: first polyrhythm value return mo; } ////////////////////////////// // // destroy_object -- what to do when the object is deleted from the patch. // In this case, you must free the clock objects after stopping // them. // void destroy_object(MyObject *mo) { MessageStop(mo); clock_free(mo->clock[0]); clock_free(mo->clock[1]); clock_free(mo->clock[2]); clock_free(mo->clock[3]); clock_free(mo->clock[4]); clock_free(mo->clock[5]); } ////////////////////////////////////////////////////////////////////////// // // Behavior functions: // ////////////////////////////// // // InputInt -- turn on the metronome when a non-zero positive value is // received, or turn it off when a zero or a negative value is received. // void InputInt(MyObject* mo, long value) { if (value > 0) { MessageStart(mo); } else { MessageStop(mo); } } ////////////////////////////// // // InputA -- input the first polyrhthmic cycle. // void InputA(MyObject* mo, long value) { if (value < 1) { mo->divisions[0] = 1; } else { mo->divisions[0] = value; } } ////////////////////////////// // // InputB -- input the second polyrhthmic cycle. // void InputB(MyObject* mo, long value) { if (value < 1) { mo->divisions[1] = 1; } else { mo->divisions[1] = value; } } ////////////////////////////// // // InputC -- input the third polyrhthmic cycle. // void InputC(MyObject* mo, long value) { if (value < 1) { mo->divisions[2] = 1; } else { mo->divisions[2] = value; } } ////////////////////////////// // // InputD -- input the fourth polyrhthmic cycle. // void InputD(MyObject* mo, long value) { if (value < 1) { mo->divisions[3] = 1; } else { mo->divisions[3] = value; } } ////////////////////////////// // // InputE -- input the fifth polyrhthmic cycle. // void InputE(MyObject* mo, long value) { if (value < 1) { mo->divisions[4] = 1; } else { mo->divisions[4] = value; } } ////////////////////////////// // // InputF -- input the sixth polyrhthmic cycle. // void InputF(MyObject* mo, long value) { if (value < 1) { mo->divisions[5] = 1; } else { mo->divisions[5] = value; } } ////////////////////////////// // // InputBang -- What to do when a bang message arrives into the object: // if a bang is received from the object input, then send out a bang. // void InputBang(MyObject* mo) { MessageReset(mo); } ////////////////////////////// // // InputDuration -- // void InputDuration(MyObject* mo, float value) { if (value < 1.0) { value = 1.0; } mo->duration = value; } ////////////////////////////// // // ClockStartCycle -- what to do when the event scheduler is ready // to play a new cycle in the polyrhythm generation. // void ClockStartCycle(MyObject *mo) { MessageStart(mo); } ////////////////////////////// // // ClockActionA -- what to do when the event scheduler is ready // to play the first polymetric channel. // void ClockActionA(MyObject *mo) { if (mo->current[0] >= mo->divisions[0]-1) { clock_fdelay(mo->clockStartCycle, mo->duration); } else { clock_fdelay(mo->clock[0], mo->duration); } outlet_int(mo->output[0], ++mo->current[0]); } ////////////////////////////// // // ClockActionB -- what to do when the event scheduler is ready // to play the second polymetric channel. // void ClockActionB(MyObject *mo) { if (mo->current[1] < mo->divisions[1]) { clock_fdelay(mo->clock[1], mo->duration * mo->divisions[0] / mo->divisions[1]); } outlet_int(mo->output[1], ++mo->current[1]); } ////////////////////////////// // // ClockActionC -- what to do when the event scheduler is ready // to play the second polymetric channel. // void ClockActionC(MyObject *mo) { if (mo->current[2] < mo->divisions[2]) { clock_fdelay(mo->clock[2], mo->duration * mo->divisions[0] / mo->divisions[2]); } outlet_int(mo->output[2], ++mo->current[2]); } ////////////////////////////// // // ClockActionD -- what to do when the event scheduler is ready // to play the second polymetric channel. // void ClockActionD(MyObject *mo) { if (mo->current[3] < mo->divisions[3]) { clock_fdelay(mo->clock[3], mo->duration * mo->divisions[0] / mo->divisions[3]); } outlet_int(mo->output[3], ++mo->current[3]); } ////////////////////////////// // // ClockActionE -- what to do when the event scheduler is ready // to play the second polymetric channel. // void ClockActionE(MyObject *mo) { if (mo->current[4] < mo->divisions[4]) { clock_fdelay(mo->clock[4], mo->duration * mo->divisions[0] / mo->divisions[4]); } outlet_int(mo->output[4], ++mo->current[4]); } ////////////////////////////// // // ClockActionF -- what to do when the event scheduler is ready // to play the second polymetric channel. // void ClockActionF(MyObject *mo) { if (mo->current[5] < mo->divisions[5]) { clock_fdelay(mo->clock[5], mo->duration * mo->divisions[0] / mo->divisions[5]); } outlet_int(mo->output[5], ++mo->current[5]); } ////////////////////////////// // // MessageStop -- what to do when a "stop" message arrives // void MessageStop(MyObject *mo) { int i; for (i=0; iclock[i]); } } ////////////////////////////// // // MessageStart -- what to do when a "start" message arrives // void MessageStart(MyObject *mo) { int i; for (i=0; iclock[i], 0.0); mo->current[i] = 0; } } ////////////////////////////// // // MessageReset -- what to do when a "reset" message arrives. // void MessageReset(MyObject *mo) { MessageStop(mo); MessageStart(mo); }