Assignment - Week 3

Copy and modify Examples/MusicKit/MidiEcho to do some other type of MKNoteFilter processing on MIDI data.

The following is an example MKNoteFilter:

/* This class is a MKNoteFilter that generates echoes and sends them to 
   its successive MKNoteSenders. In MyApp, we connect the MKNoteSenders to 
   the MKNoteReceivers of MKMidi, thus producing MIDI echoes on successive
   MIDI channels. To use this app, you need to have a MIDI synthesizer that
   can receive on multiple channels, such as the Yamaha SY77 or FB01. */

#import <MusicKit/MusicKit.h>
#import "EchoFilter.h"
#define NUMCHANS 8  /* My MIDI Synthesizer handles 8 channels. */

@implementation EchoFilter : MKNoteFilter
  /* A simple note filter that does MIDI echo */
{
    double delay;		    /* delay between echos, in seconds */
}

- init
  /* Called automatically when an instance is created. */
{    int i;
     
     [super init]; 
     delay = .1;
     for (i = 0; i <= NUMCHANS; i++)  /* 1 for each channel plus 'sys' messages */
         [self addNoteSender: [[MKNoteSender alloc] init]];
     [self addNoteReceiver: [[MKNoteReceiver alloc] init]];
     return self;
 }

- setDelay: (double)delayArg
  /* change the amount of delay (in seconds) between echoes */
{
    delay = delayArg;
    return self;
}

- connectAcross: anInstOrNoteFilter    
  /* Just connects successive MKNoteSenders of the receivers to successive
     MKNoteReceivers of anInstOrNoteFilter. */
{
    NSArray *pList = [self noteSenders];
    NSArray *iList = [anInstOrNoteFilter noteReceivers];
    int i,siz;
    int pSiz = [pList count];
    int iSiz = [iList count];
    siz = (pSiz > iSiz) ? iSiz : pSiz;   /* Take min length */
    for (i = 0; i < siz; i++)            /* Connect them up */
        [[pList objectAtIndex: i] connect: [iList objectAtIndex: i]];
    return self;
}

#define NOTESENDER(_i) [noteSenders objectAtIndex: _i]

- realizeNote: aNote fromNoteReceiver: aNoteReceiver
  /* Here's where the work is done. */
{
    /* This relies on the knowledge that the MKMidi object sorts its incoming 
       notes by channel as well as by noteTag. Thus, duplicating a note with
       a particular noteTag on several channels works ok. In general, this 
       MKNoteFilter assumes each output (MKNoteSender) is assigned a unique
       connectio (MKNoteReceiver). */
       
    int i;
    double curDly;
    int velocity, noteType;
    id newNote;

    noteType = [aNote noteType];
    if (noteType == MK_mute) {	
        [NOTESENDER(0) sendNote: aNote];          /* Just forward these */
        return self;
    }
    curDly = 0;
    [NOTESENDER(1) sendNote: aNote];             /* Send current note */
    velocity = [aNote parAsInt: MK_velocity];    /* Grab velocity */
    for (i = 2; i <= NUMCHANS; i++) {         /* Make echoes */	
        curDly += delay;                         	
        newNote = [aNote copy];                  /* Need to copy notes here */
        if (noteType == MK_noteOn)               /* Decrement echo velocity */
            [newNote setPar: MK_velocity toInt: velocity -= 15];	
        /* Schedule it for later */	
        [NOTESENDER(i) sendAndFreeNote: newNote withDelay: curDly];
    }
    return self;
}

@end