|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.