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