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