Annotation of Examples/SoundAndMusic/MidiDriver/recordmidifile.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * This example test program records MIDI data as a standard Level 0 Midifile
        !             3:  * as defined by the Midi Manufacturer's Association.
        !             4:  *
        !             5:  * Original written by Gregg Kellogg and Danna Massie.
        !             6:  * Rewritten for release 3.0 MIDI driver by David Jaffe.
        !             7:  */
        !             8: 
        !             9: #import <mach/mach.h>
        !            10: #import <stdio.h>
        !            11: #import <stdlib.h>
        !            12: #import <mach/mach_error.h>
        !            13: #import <signal.h>
        !            14: #import <servers/netname.h>
        !            15: #import <libc.h>
        !            16: #import <appkit/nextstd.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 dataPort;      /* Port for incoming data. */
        !            24: static port_t exceptionPort; /* Port for timing exceptions */
        !            25: static port_t alarmPort;     /* To get periodic messages. */
        !            26: static NXStream *s;          /* Stream for reading input file */
        !            27: static int unit = MIDI_PORT_A_UNIT;/* Serial port to read from */
        !            28: static int byteCount = 0;
        !            29: 
        !            30: static boolean_t verbose;    /* Flag. */
        !            31: 
        !            32: /* Forward references */
        !            33: static void usage(void);
        !            34: static void checkForError(char *msg,int errorReturn);
        !            35: static port_t allocPort(void);
        !            36: static void initFile(int fd);
        !            37: static void myExceptionReply(port_t replyPort, int exception);
        !            38: static void myAlarmReply(port_t replyPort, int requestedTime, int actualTime); 
        !            39: static void myDataReply(port_t replyPort, short unit, MIDIRawEvent *events, unsigned int count);
        !            40: static void cleanup();
        !            41: static port_t createPortSet(port_t dataPort, port_t exceptionPort,
        !            42:                            port_t alarmPort);
        !            43: static int fd;
        !            44: 
        !            45: main(int argc, char **argv)
        !            46: {
        !            47:     int i;
        !            48:     kern_return_t r;
        !            49:     int synchToTimeCode = FALSE;
        !            50:     port_set_name_t ports;   /* Port set to listen for messages from driver */
        !            51:     int synchUnit;
        !            52:     char *filename = NULL;     
        !            53:     MIDIReplyFunctions funcs = {0};
        !            54:     signal (SIGINT, cleanup); /* Control-C routine to clean up gracefully */
        !            55:     while ((i = getopt(argc, argv, "p:f:s:v")) != EOF) {
        !            56:        switch (i) {
        !            57:        case 'p':
        !            58:            unit = (!strcmp (optarg ,"a")) ? MIDI_PORT_A_UNIT : MIDI_PORT_B_UNIT;
        !            59:            break;
        !            60:        case 'f':
        !            61:            filename = optarg ;
        !            62:            break;
        !            63:        case 's':
        !            64:            synchToTimeCode = TRUE;
        !            65:            synchUnit = (!strcmp (optarg ,"a")) ? MIDI_PORT_A_UNIT : MIDI_PORT_B_UNIT;
        !            66:            break;
        !            67:        case 'v':
        !            68:            verbose = 1;
        !            69:            break;
        !            70:        case 'h':
        !            71:        case '?':
        !            72:        default:
        !            73:            usage();
        !            74:            exit(1);
        !            75:        }
        !            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: 
        !            90:     if ((fd = open(filename, O_WRONLY | O_CREAT, 0644)) < 0) {
        !            91:         fprintf(stderr,"open failed on filename %s\n", filename);
        !            92:        exit(1);
        !            93:     }
        !            94:     initFile(fd);
        !            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(dataPort=allocPort(), exceptionPort=allocPort(),
        !           114:                          alarmPort=allocPort());
        !           115:     r = MIDIRequestExceptions(driverPort, ownerPort, exceptionPort);
        !           116:     checkForError("MIDIRequestExceptions",r);
        !           117:     /*
        !           118:      * Tell it to ignore system real time messages we're not interested in.
        !           119:      */
        !           120:     r = MIDISetSystemIgnores(driverPort,ownerPort,unit,MIDI_IGNORE_REAL_TIME);
        !           121:     checkForError("MIDISetSysIgnores",r);
        !           122:     /* Ask for data */
        !           123:     r = MIDIRequestData(driverPort,ownerPort,unit,dataPort);
        !           124:     checkForError("MIDIRequestData",r);
        !           125:     /* Request a message at time 1000ms (1 second). */
        !           126:     r = MIDIRequestAlarm(driverPort,ownerPort,alarmPort,1000);
        !           127:     checkForError("MIDIRequestAlarm",r);
        !           128:     if (!synchToTimeCode) {
        !           129:        r = MIDISetClockTime(driverPort, ownerPort, 0);
        !           130:        checkForError("MIDISetClockTime",r);
        !           131:        /* We start clock now.  Alternatively, we could first queue up
        !           132:         * some messages and then start time.  That would insure that
        !           133:         * the first few notes come out correctly.  
        !           134:         */
        !           135:        r = MIDIStartClock(driverPort, ownerPort);
        !           136:        checkForError("MIDIStartTime",r);
        !           137:     }
        !           138:     /* Note: If this code is included in an Application, you must either
        !           139:      * run MIDIAwaitReply() in a separate Mach thread or use MIDIHandleReply()
        !           140:      * instead of MIDIAwaitReply() and register the port set with DPSAddPort().
        !           141:      * See <mididriver/midi_driver.h> for details. 
        !           142:      */        
        !           143:     funcs.exceptionReply = myExceptionReply;
        !           144:     funcs.dataReply = myDataReply;
        !           145:     funcs.alarmReply = myAlarmReply;
        !           146:     if (synchToTimeCode) 
        !           147:        fprintf(stderr,"Waiting for time code to start...\n");
        !           148:     for (;;) {
        !           149:        r = MIDIAwaitReply(ports,&funcs,MIDI_NO_TIMEOUT);
        !           150:        checkForError("MIDIAwaitReply",r);
        !           151:     }
        !           152: }
        !           153: 
        !           154: void usage(void)
        !           155: {
        !           156:     fprintf(stderr,
        !           157:            "usage: recordmidifile -f file.midi [-p {A, B}] [-s {A, B}] [-v]\n"
        !           158:            "       -p is the serial port to receive the MIDI from\n"
        !           159:            "       -v means verbose\n"
        !           160:            "       -s is the serial port to receive MIDI time code, if any.\n");
        !           161: }
        !           162: 
        !           163: static void *fileStruct;
        !           164: 
        !           165: enum {full,fullSysex,incomplete};
        !           166: 
        !           167: static int midiParser(unsigned char aByte)
        !           168:     /* Returns TRUE when a valid MIDI message is parsed.  We don't need
        !           169:      * to worry about System Real Time messages because they're filtered
        !           170:      * out by the driver. */
        !           171: {
        !           172:     #define NO_RUNNING_STATUS 0
        !           173:     static unsigned char runningStatus = NO_RUNNING_STATUS;
        !           174:     static int op = 0;
        !           175:     static int nBytes = 0;
        !           176:     static int msgSize = 0;
        !           177:     int ret;
        !           178:     if (aByte & MIDI_STATUSBIT) {
        !           179:        if (aByte == MIDI_EOX && op == MIDI_SYSEXCL)
        !           180:            return fullSysex;
        !           181:        op = aByte;
        !           182:        if (MIDI_TYPE_SYSTEM(aByte))
        !           183:            runningStatus = NO_RUNNING_STATUS;
        !           184:        else runningStatus = op;
        !           185:        msgSize = MIDI_EVENTSIZE(runningStatus);
        !           186:        nBytes = 0;
        !           187:     }
        !           188:     ret = ((op == MIDI_SYSEXCL) ? incomplete : 
        !           189:            (++nBytes == msgSize) ? full : incomplete);
        !           190:     if (ret == full)
        !           191:         nBytes = 1;
        !           192:     return ret;
        !           193: }
        !           194: 
        !           195: static char *setBufferSize(char *bytes,int size)
        !           196: {
        !           197:     static int bufsize = 0;
        !           198:     if (!bytes) 
        !           199:        NX_MALLOC(bytes,char,bufsize = size);
        !           200:     else if (size < bufsize) {
        !           201:        bufsize = size * 2;
        !           202:        NX_REALLOC(bytes,char,bufsize);
        !           203:     }
        !           204:     return bytes;
        !           205: }
        !           206: 
        !           207: static void myDataReply(port_t replyPort, short unit, MIDIRawEvent *events, unsigned int count)
        !           208:     /* This gets invoked when data comes in. */
        !           209: {
        !           210:     static int byteIndex;
        !           211:     int i;
        !           212:     MIDIRawEvent *p;
        !           213:     if (verbose) {
        !           214:        for (i = 0, p = events; i < count; i++, p++)
        !           215:            fprintf(stderr,"0x%x@%d ", p->byte, p->time);
        !           216:        fprintf(stderr,"\n");
        !           217:     }
        !           218:     byteCount += count;
        !           219:     for (p = events; count--; p++) {
        !           220:        static char *bytes = NULL;
        !           221:        bytes = setBufferSize(bytes,byteIndex+1);
        !           222:        bytes[byteIndex++] = p->byte;
        !           223:        switch (midiParser(p->byte)) {
        !           224:          case full:
        !           225:            MIDIFILEWriteEvent(fileStruct,p->time,byteIndex,bytes);
        !           226:            byteIndex = 0;
        !           227:            break;
        !           228:          case fullSysex:
        !           229:            MIDIFILEWriteSysExcl(fileStruct,p->time,byteIndex,bytes);
        !           230:            byteIndex = 0;
        !           231:            break;
        !           232:          default:
        !           233:            break;
        !           234:        }
        !           235:     }
        !           236: }
        !           237: 
        !           238: static void myAlarmReply(port_t replyPort, int requestedTime, int actualTime) 
        !           239:     /* This gets invoked when an alarm occurs. */ 
        !           240: {
        !           241:     kern_return_t r;
        !           242:     fprintf(stderr,"Time = %d ms\n",requestedTime);
        !           243:     r = MIDIRequestAlarm(driverPort,ownerPort,alarmPort,requestedTime+1000);
        !           244:     checkForError("MIDIRequestAlarm",r);
        !           245: }
        !           246: 
        !           247: static void myExceptionReply(port_t replyPort, int exception)
        !           248:     /* This gets invoked when exceptions occur. */
        !           249: {
        !           250:     switch (exception) {
        !           251:       case MIDI_EXCEPTION_MTC_STOPPED:
        !           252:        fprintf(stderr,"MIDI time code stopped.\n");
        !           253:        break;
        !           254:       case MIDI_EXCEPTION_MTC_STARTED_FORWARD:
        !           255:        fprintf(stderr,"MIDI time code started (forward).\n");
        !           256:        break;
        !           257:       case MIDI_EXCEPTION_MTC_STARTED_REVERSE:
        !           258:        fprintf(stderr,"MIDI time code started (reverse).\n");
        !           259:        break;
        !           260:       default:
        !           261:        break;
        !           262:     }
        !           263: }
        !           264: 
        !           265: static void checkForError(char *msg,int errorReturn)
        !           266:     /* Checks for error.  If error, prints message and quits. */
        !           267: {
        !           268:     if (errorReturn != KERN_SUCCESS) {
        !           269:        switch (errorReturn) {
        !           270:           case MIDI_ERROR_BUSY:
        !           271:            printf("%s: %s",msg,"MIDI driver busy.\n");
        !           272:            break;
        !           273:           case MIDI_ERROR_NOT_OWNER:
        !           274:            printf("%s: %s",msg,"You must be owner of the MIDI driver.\n");
        !           275:            break;
        !           276:           case MIDI_ERROR_QUEUE_FULL:
        !           277:            printf("%s: %s",msg,"MIDI driver queue full.\n");
        !           278:            break;
        !           279:           case MIDI_ERROR_BAD_MODE:
        !           280:            printf("%s: %s",msg,"Bad MIDI driver clock mode.\n");
        !           281:            break;
        !           282:           case MIDI_ERROR_UNIT_UNAVAILABLE:
        !           283:            printf("%s: %s",msg,"MIDI driver unit unavailable.\n");
        !           284:            break;
        !           285:           case MIDI_ERROR_ILLEGAL_OPERATION:
        !           286:            printf("%s: %s",msg,"MIDI driver illegal operation.\n");
        !           287:            break;
        !           288:          default: 
        !           289:            mach_error(msg,errorReturn);
        !           290:        }
        !           291:        exit(1);
        !           292:     }
        !           293: }
        !           294: 
        !           295: static port_t allocPort(void)
        !           296:     /* Allocates a port and returns it. */
        !           297: {
        !           298:     port_t aPort;
        !           299:     int r = port_allocate(task_self(), &aPort);
        !           300:     checkForError("allocPort",r);
        !           301:     return aPort;
        !           302: }
        !           303: 
        !           304: static port_t createPortSet(port_t dataPort, port_t exceptionPort,
        !           305:                            port_t alarmPort) 
        !           306:     /* Creates the port set and adds the three ports.  */
        !           307: {
        !           308:     port_set_name_t aPortSet;
        !           309:     int r = port_set_allocate(task_self(), &aPortSet);
        !           310:     checkForError("createPortSet",r);
        !           311:     r = port_set_add(task_self(), aPortSet, dataPort);
        !           312:     checkForError("createPortSet",r);
        !           313:     r = port_set_add(task_self(), aPortSet, alarmPort);
        !           314:     checkForError("createPortSet",r);
        !           315:     r = port_set_add(task_self(), aPortSet, exceptionPort);
        !           316:     checkForError("createPortSet",r);
        !           317:     return aPortSet;
        !           318: }
        !           319: 
        !           320: static void initFile(int fd)
        !           321:     /* Set up file. */
        !           322: {
        !           323:     s = NXOpenFile(fd,NX_WRITEONLY);
        !           324:     fileStruct = MIDIFILEBeginWriting(s,0,NULL);
        !           325:     MIDIFILEWriteTempo(fileStruct,0,60);
        !           326:     MIDIFILEBeginWritingTrack(fileStruct,NULL);
        !           327: }
        !           328: 
        !           329: void cleanup()
        !           330:     /* we kill this test program by control-C; must save midifile */
        !           331: {
        !           332:     fprintf(stderr,"Received %d MIDI bytes\n",byteCount);    
        !           333:     MIDIReleaseOwnership(driverPort,ownerPort);
        !           334:     MIDIFILEEndWritingTrack(fileStruct,0);
        !           335:     MIDIFILEEndWriting(fileStruct);
        !           336:     close(fd);
        !           337:     exit(0);
        !           338: }
        !           339: 

unix.superglobalmegacorp.com

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