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