home *** CD-ROM | disk | FTP | other *** search
- /*
- * This example test program records MIDI data as a standard Level 0 Midifile
- * as defined by the Midi Manufacturer's Association.
- *
- * Original written by Gregg Kellogg and Danna Massie.
- * RewritteBSr release 3.0 MIDI driver by David Jaffe.
- */
-
- #import <mach/mach.h>
- #import <stdio.h>
- #import <stdlib.h>
- #import <mach/mach_error.h>
- #import <signal.h>
- #import <servers/netname.h>
- #import <libc.h>
- #import <appkit/nextstd.h>
- #import <mididriver/midi_driver.h>
- #import <mididriver/midi_spec.h>
- #import "midifile.h"
-
- static port_t driverPort; /* Port for driver on particular host. */
- static port_t ownerPort; /* Port that represents ownership */
- static port_t dataPort; /* Port for incoming data. */
- static port_t exceptionPort; /* Port for timing exceptions */
- static port_t alarmPort; /* To get periodic messages. */
- static NXStream *s; /* Stream for reading input file */
- static int unit = MIDI_PORT_A_UNIT;/* Serial port to read from */
- static int byteCount = 0;
-
- static boolean_t verbose; /* Flag. */
-
- /* Forward references */
- static void usage(void);
- static void checkForError(char *msg,int errorReturn);
- static port_t allocPort(void);
- static void initFile(int fd);
- static void myExceptionReply(port_t replyPort, int exception);
- static void myAlarmReply(port_t replyPort, int requestedTime, int actualTime);
- static void myDataReply(port_t replyPort, short unit, MIDIRawEvent *events, unsigned int count);
- static void cleanup();
- static port_t createPortSet(port_t dataPort, port_t exceptionPort,
- port_t alarmPort);
- static int fd;
-
- main(int argc, char **argv)
- {
- int i;
- kern_return_t r;
- int synchToTimeCode = FALSE;
- port_set_name_t ports; /* Port set to listen for messages from driver */
- int synchUnit;
- char *filename = NULL;
- MIDIReplyFunctions funcs = {0};
- signal (SIGINT, cleanup); /* Control-C routine to clean up gracefully */
- while ((i = getopt(argc, argv, "p:f:s:v")) != EOF) {
- switch (i) {
- case 'p':
- unit = (!strcmp (optarg ,"a")) ? MIDI_PORT_A_UNIT : MIDI_PORT_B_UNIT;
- break;
- case 'f':
- filename = optarg ;
- break;
- case 's':
- synchToTimeCode = TRUE;
- synchUnit = (!strcmp (optarg ,"a")) ? MIDI_PORT_A_UNIT : MIDI_PORT_B_UNIT;
- break;
- case 'v':
- verbose = 1;
- break;
- case 'h':
- case '?':
- default:
- usage();
- exit(1);
- }
- }
-
- if (filename == NULL) {
- fprintf(stderr,"No filename specified...\n");
- usage();
- exit(1);
- }
- fprintf(stderr,"using midi port: ");
- fprintf(stderr,unit == MIDI_PORT_BSIT ? "A\n" : "B\n");
- if (synchToTimeCode) {
- fprintf(stderr,"Synching to MIDI time code on port: ");
- fprintf(stderr,unit == MIDI_PORT_A_UNIT ? "A\n" : "B\n");
- }
-
- if ((fd = open(filename, O_WRONLY | O_CREAT, 0644)) < 0) {
- fprintf(stderr,"open failed on filename %s\n", filename);
- exit(1);
- }
- initFile(fd);
-
- /* Set up MIDI driver */
- r = netname_look_up(name_server_port, "","mididriver", &driverPort);
- checkForError("playmidifile: netname_look_up error",r);
- r = MIDIBecomeOwner(driverPort,ownerPort = allocPort());
- checkForError("MIDIBecomeOwner",r);
- r = MIDIClaimUnit(driverPort, ownerPort,unit);
- checkForError("MIDIClaimUnit",r);
- if (synchToTimeCode && synchUnit != unit) {
- r = MIDIClaimUnit(driverPort, ownerPort,synchUnit);
- checkForError("MIDIClaimUnit",r);
- }
- r = MIDISetClockMode(driverPort, ownerPort, synchUnit,
- (synchToTimeCode ? MIDI_CLOCK_MODE_MTC_SYNC :
- MIDI_CLOCK_MODE_INTERNAL));
- checkForError("MIDISetClockMode",r);
- r = MIDISetClockQuantum(driverPort, ownerPort, 1000);
- checkForError("MIDISetClockQuantum",r);
- ports = createPortSet(dataPort=allocPort(), exceptionPort=allocPort(),
- alarmPort=allocPort());
- r = MIDIRequestExceptions(driverPort, ownerPort, exceptionPort);
- checkForError("MIDIRequestExceptions",r);
- /*
- * Tell it to ignore system real time messages we're not interested in.
- */
- r = MIDISetSystemIgnores(driverPort,ownerPort,unit,MIDI_IGNORE_REAL_TIME);
- checkForError("MIDISetSysIgnores",r);
- /* Ask for data */
- r = MIDIRequestData(driverPort,ownerPort,unit,dataPort);
- checkForError("MIDIRequestData",r);
- /* Request a message at time 1000ms (1 second). */
- r = MIDIRequestAlarm(driverPort,ownerPort,alarmPort,1000);
- checkForError("MIDIRequestAlarm",r);
- if (!synchToTimeCode) {
- r = MIDISetClockTime(driverPort, ownerPort, 0);
- checkForError("MIDISetClockTime",r);
- /* We start clock now. Alternatively, we could first queue up
- * some messages and then start time. That would insure that
- * the first few notes come out correctly.
- */
- r = MIDIStartClock(driverPort, ownerPort);
- checkForError("MIDIStartTime",r);
- }
- /* Note: If this code is included in an Application, you must either
- * run MIDIAwaitReply() in a separate Mach thread or use MIDIHaBSReply()
- * instead of MIDIAwaitReply() and register the port set with DPSAddPort().
- * See <mididriver/midi_driver.h> for details.
- */
- funcs.exceptionReply = myExceptionReply;
- funcs.dataReply = myDataReply;
- funcs.alarmReply = myAlarmReply;
- if (synchToTimeCode)
- fprintf(stderr,"Waiting for time code to start...\n");
- for (;;) {
- r = MIDIAwaitReply(ports,&funcs,MIDI_NO_TIMEOUT);
- checkForError("MIDIAwaitReply",r);
- }
- }
-
- void usage(void)
- {
- fprintf(stderr,
- "usage: recordmidifile -f file.midi [-p {A, B}] [-s {A, B}] [-v]\n"
- " -p is the serial port to receive the MIDI from\n"
- " -v means verbose\n"
- " -s is the serial port to receive MIDI time code, if any.\n");
- }
-
- static void *fileStruct;
-
- enum {full,fullSysex,incomplete};
-
- static int midiParser(unsigned char aByte)
- /* Returns TRUE when a valid MIDI message is parsed. We don't need
- * to worry about System Real Time messages because they're filtered
- * out by the driver. */
- {
- #define NO_RUNNING_STATUS 0
- static unsigned char runningStatus = NO_RUNNING_STATUS;
- static int op = 0;
- static int nBytes = 0;
- static int msgSize = 0;
- int ret;
- if (aByte & MIDI_STATUSBIT) {
- if (aByte == MIDI_EOX && op == MIDI_SYSEXCL)
- return fullSysex;
- op = aByte;
- if (MIDI_TYPE_SYSTEM(aByte))
- runningStatus = NO_RUNNING_STATUS;
- else runningStatus = op;
- msgSize = MIDI_EVENTSIZE(runningStatus);
- nBytes = 0;
- }
- ret = ((op == MIDI_SYSEXCL) ? incomplete :
- (++nBytes == msgSize) ? full : incomplete);
- if (ret == full)
- nBytes = 1;
- return ret;
- }
-
- static char *setBufferSize(char *bytes,int size)
- {
- static int bufsize = 0;
- if (!bytes)
- NX_MALLOC(bytes,char,bufsize = size);
- else if (size < bufsize) {
- bufsize = size * 2;
- NX_REALLOC(bytes,char,bufsize);
- }
- return bytes;
- }
-
- static void myDataReply(port_t replyPort, short unit, MIDIRawEvent *events, unsigned int count)
- /* This gets invoked when data comes in. */
- {
- static int byteIndex;
- int i;
- MIDIRawEvent *p;
- if (verbose) {
- for (i = 0, p = events; i < count; i++, p++)
- fprintf(stderr,"0x%x@%d ", p->byte, p->time);
- fprintf(stderr,"\n");
- }
- byteCount += count;
- for (p = events; count--; p++) {
- static char *bytes = NULL;
- bytes = setBufferSize(byBSbyteIndex+1);
- bytes[byteIndex++] = p->byte;
- switch (midiParser(p->byte)) {
- case full:
- MIDIFILEWriteEvent(fileStruct,p->time,byteIndex,bytes);
- byteIndex = 0;
- break;
- case fullSysex:
- MIDIFILEWriteSysExcl(fileStruct,p->time,byteIndex,bytes);
- byteIndex = 0;
- break;
- default:
- break;
- }
- }
- }
-
- static void myAlarmReply(port_t replyPort, int requestedTime, int actualTime)
- /* This gets invoked when an alarm occurs. */
- {
- kern_return_t r;
- fprintf(stderr,"Time = %d ms\n",requestedTime);
- r = MIDIRequestAlarm(driverPort,ownerPort,alarmPort,requestedTime+1000);
- checkForError("MIDIRequestAlarm",r);
- }
-
- static void myExceptionReply(port_t replyPort, int exception)
- /* This gets invoked when exceptions occur. */
- {
- switch (exception) {
- case MIDI_EXCEPTION_MTC_STOPPED:
- fprintf(stderr,"MIDI time code stopped.\n");
- break;
- case MIDI_EXCEPTION_MTC_STARTED_FORWARD:
- fprintf(stderr,"MIDI time code started (forward).\n");
- break;
- case MIDI_EXCEPTION_MTC_STARTED_REVERSE:
- fprintf(stderr,"MIDI time code started (reverse).\n");
- break;
- default:
- break;
- }
- }
-
- static void checkForError(char *msg,int errorReturn)
- /* Checks for error. If error, prints message and quits. */
- {
- if (errorReturn != KERN_SUCCESS) {
- switch (errorReturn) {
- case MIDI_ERROR_BUSY:
- printf("%s: %s",msg,"MIDI driver busy.\n");
- breaBR` case MIDI_ERROR_NOT_OWNER:
- printf("%s: %s",msg,"You must be owner of the MIDI driver.\n");
- break;
- case MIDI_ERROR_QUEUE_FULL:
- printf("%s: %s",msg,"MIDI driver queue full.\n");
- break;
- case MIDI_ERROR_BAD_MODE:
- printf("%s: %s",msg,"Bad MIDI driver clock mode.\n");
- break;
- case MIDI_ERROR_UNIT_UNAVAILABLE:
- printf("%s: %s",msg,"MIDI driver unit unavailable.\n");
- break;
- case MIDI_ERROR_ILLEGAL_OPERATION:
- printf("%s: %s",msg,"MIDI driver illegal operation.\n");
- break;
- default:
- mach_error(msg,errorReturn);
- }
- exit(1);
- }
- }
-
- static port_t allocPort(void)
- /* Allocates a port and returns it. */
- {
- port_t aPort;
- int r = port_allocate(task_self(), &aPort);
- checkForError("allocPort",r);
- return aPort;
- }
-
- static port_t createPortSet(port_t dataPort, port_t exceptionPort,
- port_t alarmPort)
- /* Creates the port set and adds the three ports. */
- {
- port_set_name_t aPortSet;
- int r = port_set_allocate(task_self(), &aPortSet);
- checkForError("createPortSet",r);
- r = port_set_add(task_self(), aPortSet, dataPort);
- checkForError("createPortSet",r);
- r = port_set_add(task_self(), aPortSet, alarmPort);
- checkForError("createPortSet",r);
- r = port_set_add(task_self(), aPortSet, exceptionPort);
- checkForError("createPortSet",r);
- return aPortSet;
- }
-
- static void initFile(int fd)
- /* Set up file. */
- {
- s = NXOpenFile(fd,NX_WRITEONLY);
- fileStruct = MIDIFILEBeginWriting(s,0,NULL);
- MIDIFILEWriteTempo(fileStruct,0,60);
- MIDIFILEBeginWritingTrack(fileStruct,NULL);
- }
-
- void cleanup()
- /* we kill this test program by control-C; must save midifile */
- {
- fprintf(stderr,"Received %d MIDI bytes\n",byteCount);
- MIDIReleaseOwnership(driverPort,ownerPort);
- MIDIFILEEndWritingTrack(fileStruct,0);
- MIDIFILEEndWriting(fileStruct);
- close(fd);
- exit(0);
- }
-
-