Annotation of Examples/SoundAndMusic/MidiDriver/playmidifile.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * This example test program reads a standard Level 0 Midifile (.midi suffix)
                      3:  * as defined by the Midi Manufacturer's Association, and plays it 
                      4:  * through the MIDI Driver. Level 1 and level 2 files are not supported
                      5:  * in this example.
                      6:  *
                      7:  * Original written by Gregg Kellogg and Danna Massie.
                      8:  * Rewritten for release 3.0 MIDI driver by David Jaffe
                      9:  */
                     10: #import <mach/mach.h>
                     11: #import <stdio.h>
                     12: #import <stdlib.h>
                     13: #import <mach/mach_error.h>
                     14: #import <signal.h>
                     15: #import <servers/netname.h>
                     16: #import <libc.h>
                     17: #import <mididriver/midi_driver.h>
                     18: #import <mididriver/midi_spec.h>
                     19: #import "midifile.h"
                     20: 
                     21: static port_t driverPort;    /* Port for driver on particular host. */
                     22: static port_t ownerPort;     /* Port that represents ownership */
                     23: static port_t queuePort;     /* Port for output queue notification messages */
                     24: static port_t exceptionPort; /* Port for timing exceptions */
                     25: static int maxQueueSize;     /* Maximum output queue size */
                     26: static boolean_t allRead;    /* Flag signaling all data read from file. */
                     27: static boolean_t allSent;    /* Flag signaling all data sent to driver. */ 
                     28: static boolean_t someSent;   /* Flag signaling if  data was sent to driver.*/ 
                     29: static NXStream *s;          /* Stream for reading input file */
                     30: static int tempo = 60;
                     31: static int unit = MIDI_PORT_A_UNIT; /* Serial port to send to */
                     32: static port_set_name_t ports;/* Port set to listen for messages from driver */
                     33: 
                     34: /* Forward references */
                     35: static void usage(void);
                     36: static void checkForError(char *msg,int errorReturn);
                     37: static port_t allocPort(void);
                     38: static port_t createPortSet(port_t queuePort, port_t exceptionPort); 
                     39: static void initFile(void);
                     40: static void setTempo(int newTempo);
                     41: static void myExceptionReply(port_t replyPort, int exception);
                     42: static void myQueueReply(port_t replyPort, short unit);
                     43: static void cleanup();
                     44: 
                     45: main(int argc, char **argv)
                     46: {
                     47:     int i;
                     48:     int synchToTimeCode = FALSE;
                     49:     kern_return_t r;
                     50:     int synchUnit;           /* Serial port to listen for time code */
                     51:     char *filename = NULL;
                     52:     MIDIReplyFunctions funcs = {0};
                     53:     signal (SIGINT, cleanup); /* Control-C routine to clean up gracefully */
                     54:     while ((i = getopt(argc, argv, "p:f:t:s:")) != EOF)
                     55:        switch (i) {
                     56:        case 'p':
                     57:            unit = ((!strcmp (optarg ,"a") || !strcmp(optarg,"A")) ? 
                     58:                    MIDI_PORT_A_UNIT : MIDI_PORT_B_UNIT);
                     59:            break;
                     60:        case 'f':
                     61:            filename = optarg ;
                     62:            break;
                     63:        case 't':
                     64:            tempo = atoi(optarg) ;
                     65:            fprintf(stderr,"tempo= %d\n",tempo);
                     66:            break;
                     67:        case 's':
                     68:            synchToTimeCode = TRUE;
                     69:            synchUnit = (!strcmp (optarg ,"a")) ? MIDI_PORT_A_UNIT : MIDI_PORT_B_UNIT;
                     70:            break;
                     71:        case 'h':
                     72:        case '?':
                     73:        default:
                     74:            usage();
                     75:            exit(1);
                     76:        }
                     77:     
                     78:     if (filename == NULL) {
                     79:        fprintf(stderr,"No filename specified...\n");
                     80:        usage();
                     81:        exit(1);
                     82:     }
                     83:     fprintf(stderr,"using midi port: ");
                     84:     fprintf(stderr,unit == MIDI_PORT_A_UNIT ? "A\n" : "B\n");
                     85:     if (synchToTimeCode) {
                     86:        fprintf(stderr,"Synching to MIDI time code on port: ");
                     87:        fprintf(stderr,unit == MIDI_PORT_A_UNIT ? "A\n" : "B\n");
                     88:     }
                     89:     s = NXMapFile(filename,NX_READONLY);
                     90:     if (!s) {
                     91:        fprintf(stderr,"Cannot open file : %s\n", filename);
                     92:        exit(1);
                     93:     }
                     94:     initFile();
                     95: 
                     96:     /* Set up MIDI driver */
                     97:     r = netname_look_up(name_server_port, "","mididriver", &driverPort);
                     98:     checkForError("playmidifile: netname_look_up error",r);
                     99:     r = MIDIBecomeOwner(driverPort,ownerPort = allocPort());
                    100:     checkForError("MIDIBecomeOwner",r);
                    101:     r = MIDIClaimUnit(driverPort, ownerPort,unit);
                    102:     checkForError("MIDIClaimUnit",r);
                    103:     if (synchToTimeCode && synchUnit != unit) {
                    104:        r = MIDIClaimUnit(driverPort, ownerPort,synchUnit);
                    105:        checkForError("MIDIClaimUnit",r);
                    106:     }
                    107:     r = MIDISetClockMode(driverPort, ownerPort, synchUnit,
                    108:                         (synchToTimeCode ? MIDI_CLOCK_MODE_MTC_SYNC : 
                    109:                          MIDI_CLOCK_MODE_INTERNAL));
                    110:     checkForError("MIDISetClockMode",r);
                    111:     r = MIDISetClockQuantum(driverPort, ownerPort, 1000);
                    112:     checkForError("MIDISetClockQuantum",r);
                    113:     ports = createPortSet(queuePort=allocPort(), exceptionPort=allocPort());
                    114:     r = MIDIRequestExceptions(driverPort, ownerPort, exceptionPort);
                    115:     checkForError("MIDIRequestExceptions",r);
                    116:     r = MIDIGetAvailableQueueSize(driverPort, ownerPort, unit, &maxQueueSize);
                    117:     checkForError("MIDIGetAvailableQueueSize",r);
                    118:     if (!synchToTimeCode) {
                    119:        r = MIDISetClockTime(driverPort, ownerPort, 0);
                    120:        checkForError("MIDISetClockTime",r);
                    121:        /* We start clock now.  Alternatively, we could first queue up
                    122:         * some messages and then start time.  That would insure that
                    123:         * the first few notes come out correctly.  
                    124:         */
                    125:        r = MIDIStartClock(driverPort, ownerPort);
                    126:        checkForError("MIDIStartTime",r);
                    127:     }
                    128:     /*
                    129:      * We play the file by chaining invocations of myQueueReply().  To
                    130:      * start the process, we ask the driver to invoke myQueueReply() when
                    131:      * the queue is fully available (which will be immediately, since nothing
                    132:      * has been sent yet.)
                    133:      *
                    134:      * Note: If this code is included in an Application, you must either
                    135:      * run MIDIAwaitReply() in a separate Mach thread or use MIDIHandleReply()
                    136:      * instead of MIDIAwaitReply() and register the port set with DPSAddPort().
                    137:      * See <mididriver/midi_driver.h> for details. 
                    138:      */        
                    139:     r = MIDIRequestQueueNotification(driverPort, ownerPort, unit, queuePort, 
                    140:                                     maxQueueSize);
                    141:     checkForError("MIDIRequestQueueNotification",r);
                    142:     funcs.exceptionReply = myExceptionReply;
                    143:     funcs.queueReply = myQueueReply;
                    144:     if (synchToTimeCode) 
                    145:        fprintf(stderr,"Waiting for time code to start...\n");
                    146:     allSent = FALSE;
                    147:     allRead = FALSE;
                    148:     someSent = TRUE;
                    149:     while (!allSent) {                /* Here's where the work happens */
                    150:        r = MIDIAwaitReply(ports,&funcs,MIDI_NO_TIMEOUT);
                    151:        checkForError("MIDIAwaitReply",r);
                    152:     }
                    153: 
                    154:     /* Wait for our output to drain. */
                    155:     r = MIDIRequestQueueNotification(driverPort, ownerPort, unit, queuePort, 
                    156:                                     maxQueueSize);
                    157:     checkForError("MIDIRequestQueueNotification",r);
                    158:     funcs.queueReply = NULL;
                    159:     r = MIDIAwaitReply(ports,&funcs,MIDI_NO_TIMEOUT);
                    160:     checkForError("MIDIAwaitReply",r);
                    161:     cleanup();
                    162: }
                    163: 
                    164: static MIDIRawEvent events[MIDI_MAX_EVENT];
                    165: static byteIndex = 0;
                    166: 
                    167: static void *fileStruct;        /* Used by MIDIFILE routines. */
                    168: static MIDIFILEReadStruct fileEvent = {0};
                    169: 
                    170: static int getNextFileByte(void)
                    171: {
                    172:     static int fileEventCtr = 0;
                    173:     if (fileEventCtr == 0)
                    174:        if (!MIDIFILEReadEvent(fileStruct))
                    175:            return FALSE;
                    176:     if (fileEvent.metaEventFlag) 
                    177:        fileEventCtr = 0;
                    178:     else {
                    179:        events[byteIndex].time = fileEvent.quanta;
                    180:        events[byteIndex].byte = fileEvent.data[fileEventCtr++];
                    181:        byteIndex++;
                    182:        if (fileEventCtr == fileEvent.nData)
                    183:            fileEventCtr = 0;
                    184:     }
                    185:     return TRUE;
                    186: }
                    187: 
                    188: static int sendData(void) 
                    189:     /* Sends the data. Returns FALSE if the driver's buffer is full. */
                    190: {
                    191:     kern_return_t r = 
                    192:        MIDISendData(driverPort, ownerPort, unit, events, byteIndex);
                    193:     if (r == MIDI_ERROR_QUEUE_FULL) {
                    194:        /* Request notification when at least half the queue is available */
                    195:        r = MIDIRequestQueueNotification(driverPort, ownerPort, 
                    196:                                         unit, queuePort, 
                    197:                                         maxQueueSize/2);
                    198:        checkForError("MIDIRequestQueueNotification",r);
                    199:        return FALSE;
                    200:     } else checkForError("MIDISendData",r);
                    201:     byteIndex = 0;
                    202:     return TRUE;
                    203: }
                    204: 
                    205: static void myQueueReply(port_t replyPort, short unit)
                    206:     /* This gets invoked when the queue has enough room for more data. */
                    207: {
                    208:     for (;;) {
                    209:        if (byteIndex == MIDI_MAX_EVENT-1)
                    210:            if (!sendData())
                    211:                return; 
                    212:        if (!allRead) 
                    213:            allRead = !getNextFileByte();
                    214:        if (allRead) {
                    215:            if (sendData()) 
                    216:                allSent = TRUE;
                    217:            return;
                    218:        }
                    219:     }
                    220: }
                    221: 
                    222: static void myExceptionReply(port_t replyPort, int exception)
                    223:     /* This gets invoked when exceptions occur. */
                    224: {
                    225:     switch (exception) {
                    226:       case MIDI_EXCEPTION_MTC_STOPPED:
                    227:        fprintf(stderr,"MIDI time code stopped.\n");
                    228:        break;
                    229:       case MIDI_EXCEPTION_MTC_STARTED_FORWARD:
                    230:        fprintf(stderr,"MIDI time code started (forward).\n");
                    231:        break;
                    232:       case MIDI_EXCEPTION_MTC_STARTED_REVERSE:
                    233:        fprintf(stderr,"MIDI time code started (reverse).\n");
                    234:        break;
                    235:       default:
                    236:        break;
                    237:     }
                    238: }
                    239: 
                    240: static void usage(void)
                    241: {
                    242:     fprintf(stderr,
                    243:            "usage: playmidifile -f file.midi [-p {A, B}] [-s {A, B}]\n"
                    244:            "       -p is the serial port to send the MIDI\n"
                    245:            "       -s is the serial port to receive MIDI time code, if any.\n");
                    246: }
                    247: 
                    248: static void checkForError(char *msg,int errorReturn)
                    249:     /* Checks for error.  If error, prints message and quits. */
                    250: {
                    251:     if (errorReturn != KERN_SUCCESS) {
                    252:        switch (errorReturn) {
                    253:           case MIDI_ERROR_BUSY:
                    254:            printf("%s: %s",msg,"MIDI driver busy.\n");
                    255:            break;
                    256:           case MIDI_ERROR_NOT_OWNER:
                    257:            printf("%s: %s",msg,"You must be owner of the MIDI driver.\n");
                    258:            break;
                    259:           case MIDI_ERROR_QUEUE_FULL:
                    260:            printf("%s: %s",msg,"MIDI driver queue full.\n");
                    261:            break;
                    262:           case MIDI_ERROR_BAD_MODE:
                    263:            printf("%s: %s",msg,"Bad MIDI driver clock mode.\n");
                    264:            break;
                    265:           case MIDI_ERROR_UNIT_UNAVAILABLE:
                    266:            printf("%s: %s",msg,"MIDI driver unit unavailable.\n");
                    267:            break;
                    268:           case MIDI_ERROR_ILLEGAL_OPERATION:
                    269:            printf("%s: %s",msg,"MIDI driver illegal operation.\n");
                    270:            break;
                    271:          default: 
                    272:            mach_error(msg,errorReturn);
                    273:        }
                    274:        exit(1);
                    275:     }
                    276: }
                    277: 
                    278: static port_t allocPort(void)
                    279:     /* Allocates a port and returns it. */
                    280: {
                    281:     port_t aPort;
                    282:     int r = port_allocate(task_self(), &aPort);
                    283:     checkForError("allocPort",r);
                    284:     return aPort;
                    285: }
                    286: 
                    287: static port_t createPortSet(port_t queuePort, port_t exceptionPort) 
                    288:     /* Creates the port set and adds the two ports.  */
                    289: {
                    290:     port_set_name_t aPortSet;
                    291:     int r = port_set_allocate(task_self(), &aPortSet);
                    292:     checkForError("createPortSet",r);
                    293:     r = port_set_add(task_self(), aPortSet, queuePort);
                    294:     checkForError("createPortSet",r);
                    295:     r = port_set_add(task_self(), aPortSet, exceptionPort);
                    296:     checkForError("createPortSet",r);
                    297:     return aPortSet;
                    298: }
                    299: 
                    300: static void setTempo(int newTempo)
                    301: {
                    302:     MIDIFILESetReadQuantaSize(fileStruct,(tempo = newTempo)/60.0 * 1000);
                    303: }
                    304: 
                    305: static void initFile(void)    
                    306: {
                    307:     int level,trackCount;
                    308:     fileStruct = MIDIFILEBeginReading(s,&fileEvent);
                    309:     if (MIDIFILEReadPreamble(fileStruct,&level,&trackCount)) {
                    310:        if (level != 0) {
                    311:            fprintf(stderr,"playmidifile cannot play level %d files.\n",level);
                    312:            exit(1);
                    313:        }
                    314:     }
                    315:     else {
                    316:        fprintf(stderr,"failed to read preamble!\n");
                    317:        exit(1);
                    318:     }
                    319:     setTempo(tempo);
                    320: }
                    321: 
                    322: static void allNotesOff(void) {
                    323:     #define NOTEOFF_ARRAY_SIZE (128*2)          /* Use running status */
                    324:     MIDIRawEvent arr[NOTEOFF_ARRAY_SIZE]; 
                    325:     kern_return_t r;
                    326:     int chan = 0;
                    327:     int bytesToSend,i;
                    328:     MIDIRawEvent op;
                    329:     MIDIReplyFunctions funcs = {0};
                    330:     for (i=0; i<128; i++) {  /* Initialize array */
                    331:        arr[i*2].byte = i;   /* KeyNum */
                    332:        arr[i*2+1].byte = 0; /* Velocity */
                    333:     }
                    334:     
                    335:     while (MIDIAwaitReply(ports,&funcs,1) == KERN_SUCCESS)
                    336:        ;    /* Empty out ports of any old notification messages. */
                    337:     r = MIDIClearQueue(driverPort,ownerPort,unit);
                    338:     checkForError("MIDIClearQueue",r);
                    339:     for (chan = 0; chan < 16; chan++) {
                    340:        op.byte = MIDI_NOTEOFF | chan;
                    341:        r = MIDISendData(driverPort,ownerPort,unit,&op,1);
                    342:        checkForError("MIDISendData",r);
                    343:        for (i = 0; i < NOTEOFF_ARRAY_SIZE; i += MIDI_MAX_EVENT) {
                    344:            bytesToSend = NOTEOFF_ARRAY_SIZE - i;
                    345:            if (bytesToSend > MIDI_MAX_EVENT)
                    346:                bytesToSend = MIDI_MAX_EVENT;
                    347:            r = MIDISendData(driverPort,ownerPort,unit,&arr[i],bytesToSend);
                    348:            checkForError("MIDISendData",r);
                    349:        }
                    350:        r = MIDIFlushQueue(driverPort,ownerPort,unit);
                    351:        checkForError("MIDIFlushQueue",r);
                    352:        r = MIDIRequestQueueNotification(driverPort, ownerPort,unit,
                    353:                                         queuePort,
                    354:                                         ((chan==15) ? maxQueueSize : 
                    355:                                          NOTEOFF_ARRAY_SIZE+1));
                    356:        checkForError("MIDIRequestQueueNotification",r);
                    357:        r = MIDIAwaitReply(ports,&funcs,MIDI_NO_TIMEOUT);
                    358:        checkForError("MIDIAwaitReply",r);
                    359:     }
                    360: }
                    361: 
                    362: static void cleanup() {
                    363:     kern_return_t r;
                    364:     if (!driverPort || !ownerPort)
                    365:        exit(0);
                    366:     if (someSent)
                    367:        allNotesOff();
                    368:     r = MIDIReleaseOwnership(driverPort,ownerPort);
                    369:     checkForError("MIDIReleaseOwnership",r);
                    370:     exit(0);
                    371: }
                    372: 
                    373: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.