// // Copyright: Copyright 2003 Craig Stuart Sapp // Programmer: Craig Stuart Sapp // Creation Date: Fri Feb 7 18:12:43 PST 2003 // Last Modified: Sat Feb 8 14:33:37 PST 2003 // Filename: squelch.c // Web Address: http://peabody.sapp.org/class/dmp2/lab/squelch/squelch.c // Syntax: C; Max4/MSP2 External Object; CodeWarrior 6.0 // OS: Mac OS 9; PPC // // Description: Suppress feedback notes from the PianoDisc MIDI player // piano. The object is designed to minimize the squelch time // so that performer notes have a better change of being // recognized. Returning velocity information could be used // to increase the accuracy of identifying feedback notes. // // Currently does not do note-off suppression. It is expected // that the computer does not feedback note-offs. // #include "ext.h" #define BUFFERSZ 100 /* maximum note count that can be remembered */ typedef struct { // data structure for note memory short key; // MIDI note pitch short vel; // MIDI note velocity long mints; // minimum timestamp for expected note return long maxts; // maximum timestamp for expected note return } NoteInfo; typedef struct { t_object max_data; // Max/MSP data, MUST come first in struct NoteInfo memory[BUFFERSZ]; // storage buffer for incoming notes long writeindex; // write index of memory buffer long invel; // temporary storage for output velocity long outvel; // temporary storage for input velocity void* outputKeyNumberOut; // outlet for MIDI note number output void* outputVelocityOut; // outlet for MIDI attack velocity output void* outputKeyNumberIn; // outlet for MIDI note number input void* outputVelocityIn; // outlet for MIDI attack velocity input } MyObject; void* object_data = NULL; // function declarations: void* create_object (void); void InputKeyNumberOut (MyObject* mo, long value); void InputVelocityOut (MyObject* mo, long value); void InputKeyNumberIn (MyObject* mo, long value); void InputVelocityIn (MyObject* mo, long value); void MessageClear (MyObject* mo); long midilimit (long value); void storeNoteInMemory (MyObject* mo, long note, long timestamp); int squelched (MyObject* mo, long inkey, long curtime); void getTimeRange (long curtime, long key, long vel, long *mintime, long *maxtime); ///////////////////////////////////////////////////////////////////////// // // Initialization functions // ////////////////////////////// // // main -- called once when the object is created in a patcher window. // void main(void) { setup((t_messlist**)&object_data, (method)create_object, NULL, sizeof(MyObject), NULL, A_NOTHING); addint ((method)InputKeyNumberOut); // inlet 1 addinx ((method)InputVelocityOut, 3); // inlet 2 addinx ((method)InputKeyNumberIn, 2); // inlet 3 addinx ((method)InputVelocityIn, 1); // inlet 4 } ////////////////////////////// // // create_object -- create the data storage for the object and // and setup input 1. Default value in object is the duration // of the analysis window in seconds. // void* create_object(void) { MyObject* mo = (MyObject*)newobject(object_data); MessageClear(mo); // initialize the memory buffer and other data mo->outputVelocityIn = intout(mo); // outlet 4 mo->outputKeyNumberIn = intout(mo); // outlet 3 mo->outputVelocityOut = intout(mo); // outlet 2 mo->outputKeyNumberOut = intout(mo); // outlet 1 intin(mo, 1); // inlet 3 intin(mo, 2); // inlet 2 intin(mo, 3); // inlet 1 return mo; } ///////////////////////////////////////////////////////////////////////// // // Behavior functions // ////////////////////////////// // // InputKeyNumberOut -- The output MIDI note will be stored in memory after // being sent out of the computer. // void InputKeyNumberOut(MyObject* mo, long value) { long curtime = gettime(); outlet_int(mo->outputVelocityOut, mo->outvel); outlet_int(mo->outputKeyNumberOut, value); if (mo->outvel > 0) { storeNoteInMemory(mo, value, curtime); } } ////////////////////////////// // // InputVelocityOut -- Store the velocity and wait for the key number to come. // void InputVelocityOut(MyObject* mo, long value) { mo->outvel = value; } ////////////////////////////// // // InputKeyNumberIn -- check to see if the note should be squelched or not. // void InputKeyNumberIn(MyObject* mo, long value) { long curtime = gettime(); if (!squelched(mo, value, curtime)) { outlet_int(mo->outputVelocityIn, mo->invel); outlet_int(mo->outputKeyNumberIn, value); } } ////////////////////////////// // // InputVelocityIn -- Store the velocity and wait for the key number to come. // void InputVelocityIn(MyObject* mo, long value) { mo->invel = value; } ////////////////////////////// // // MessageClear -- remove all notes from memory buffer. // void MessageClear(MyObject* mo) { int i; for (i=0; imemory[i].mints = -1; mo->memory[i].maxts = -1; mo->memory[i].key = -1; mo->memory[i].vel = -1; } mo->writeindex = 0; } ///////////////////////////////////////////////////////////////////////// // // Non-interface functions: // ////////////////////////////// // // midilimit -- limit a number to the range from 0 to 127. // if the input is less than 0, return 0. // if the input is greater than 127, return 127. // long midilimit(long value) { if (value < 0) return 0; if (value > 127) return 127; return value; } /////////////////////////////// // // storeNoteInMemory -- // void storeNoteInMemory(MyObject* mo, long note, long timestamp) { long mintime, maxtime; getTimeRange(timestamp, midilimit(note), midilimit(mo->outvel), &mintime, &maxtime); mo->memory[mo->writeindex].key = midilimit(note); mo->memory[mo->writeindex].vel = midilimit(mo->outvel); mo->memory[mo->writeindex].mints = mintime; mo->memory[mo->writeindex].maxts = maxtime; mo->writeindex += 1; if (mo->writeindex >= BUFFERSZ) { mo->writeindex = 0; } } ////////////////////////////// // // squelched -- determine if the input note should be suppressed or not. // int squelched(MyObject* mo, long inkey, long curtime) { NoteInfo* mem = mo->memory; int i; // don't try to suppress note offs for now. if (mo->invel == 0) { return 0; } // search for a matching note and see if it is in the exected time-frame. // If so, then remove the note from memory, and return 1; for (i=0; i curtime) { mem[i].key = -1; return 1; } } return 0; } ////////////////////////////// // // getTimeRange -- calculate the minimum and maximum time the output note is // expected to return to the computer. // void getTimeRange(long curtime, long key, long vel, long *mintime, long *maxtime) { #pgagma unused(vel) #pragma unused(key) *mintime = curtime + 40; *maxtime = curtime + 250; }