|
|
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.