Digital Music Programming II: duration




This lab demonstrates how to measure the time difference between note-ons and note-offs so that the duration of notes can be determined.

The duration object given below has two inputs and two outputs:

    Inputs Outputs
    1. MIDI Note Number 1. MIDI Note Number
    2. MIDI Attack Velocity 2. duration in milliseconds

The duration object keeps track of all 128 MIDI notes and determines whether they are on or off. If an incoming note message is a note-on message, then the duration object will store the time that the note-on message came in. When a note-off message comes into the object, the time when the note was played will be subtracted from the current time which will then be sent out of the object as the note's duration in milliseconds.

To remember the note-on times, the duration object has an array of 128 longs called ontimes. When a note-on is received by the object, the time will be stored in the ontimes array based on the key number which ranges from 0 to 127. When a note-off message comes for the same key, then the object will check to see when the last note-on message came for that key in the ontimes array.

duration.c

#include "ext.h"

typedef struct {
   t_object max_data;        
   long     ontimes[127];    
   long     velin;           
   void*    outputKeyNumber; 
   void*    outputDuration;  
} MyObject;

void* object_data = NULL;

void*   create_object     (void);
void    InputKeyNumber    (MyObject* mo, long value);
void    InputVelocity     (MyObject* mo, long value);
void    MessageClear      (MyObject* mo);
long    midilimit         (long value);

void main(void) {
   setup((t_messlist**)&object_data, (method)create_object,
         NULL, sizeof(MyObject), NULL, A_NOTHING);
   addint ((method)InputKeyNumber);      
   addinx ((method)InputVelocity,  1);   
   addmess((method)MessageClear, "clear", A_NOTHING);
}

void* create_object(void) {
   MyObject* mo = (MyObject*)newobject(object_data);
   MessageClear(mo);
   mo->velin = 0;
   mo->outputDuration  = intout(mo);    
   mo->outputKeyNumber = intout(mo);    
   intin(mo, 1);                        
   return mo;
}

void InputKeyNumber(MyObject* mo, long value) {
   long curtime = gettime();
   value = midilimit(value);
   if (mo->velin == 0 && mo->ontimes[value] >= 0) {
      outlet_int(mo->outputDuration, curtime - mo->ontimes[value]);
      outlet_int(mo->outputKeyNumber, value);
      mo->ontimes[value] = -1;
   } else if (mo->velin > 0 && mo->ontimes[value] >= 0) {
      outlet_int(mo->outputDuration, curtime - mo->ontimes[value]);
      outlet_int(mo->outputKeyNumber, value);
      mo->ontimes[value] = curtime;
   } else if (mo->velin > 0) {
      mo->ontimes[value] = curtime;
   }
}

void InputVelocity(MyObject* mo, long value) {
   mo->velin = value;
}

void MessageClear(MyObject* mo) {
   int i;
   for (i=0; i<128; i++) {
      mo->ontimes[i] = -1;
   }
}

long midilimit(long value) {
   if (value < 0)     return   0;
   if (value > 127)   return 127;
   return value;
}





Exercises

  1. Create the following patch to test the duration object.

    Play somes notes and see if the notes have about the right durations. You might need to select the option Overdrive to get accurate results on a slow computer.

  2. Use the duration information from the duration object in a musical manner. For example, use the durations to control tempo, or the notes to be divided into functions depending on their durations -- such as short notes would cause staccato textures, and longer durations would cause legato textures.

  3. Take the duration output from the duration object and quantize it to a set of rhythms, such as quarter note, sixteenth note, eighth note, etc.