Annotation of Examples/SoundAndMusic/MidiDriver/playmidifile.c, revision 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.