home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NeXTSTEP 3.0
/
NeXTSTEP3.0.iso
/
NextDeveloper
/
Examples
/
SoundAndMusic
/
Drivers
/
MidiDriver
/
playmidifile.c
< prev
next >
Wrap
Text File
|
1992-04-24
|
12KB
|
374 lines
/*
* This example test program reads a standard Level 0 Midifile (.midi suffix)
* as defined by the Midi Manufacturer's Association, and plays it
* through the MIDI Driver. Level 1 and level 2 files are not supported
* in this example.
*
* Original written by Gregg Kellogg and Danna Massie.
* Rewritten for 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 <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 queuePort; /* Port for output queue notification messages */
static port_t exceptionPort; /* Port for timing exceptions */
static int maxQueueSize; /* Maximum output queue size */
static boolean_t allRead; /* Flag signaling all data read from file. */
static boolean_t allSent; /* Flag signaling all data sent to driver. */
static boolean_t someSent; /* Flag signaling if data was sent to driver.*/
static NXStream *s; /* Stream for reading input file */
static int tempo = 60;
static int unit = MIDI_PORT_A_UNIT; /* Serial port to send to */
static port_set_name_t ports;/* Port set to listen for messages from driver */
/* Forward referenBS*/
static void usage(void);
static void checkForError(char *msg,int errorReturn);
static port_t allocPort(void);
static port_t createPortSet(port_t queuePort, port_t exceptionPort);
static void initFile(void);
static void setTempo(int newTempo);
static void myExceptionReply(port_t replyPort, int exception);
static void myQueueReply(port_t replyPort, short unit);
static void cleanup();
main(int argc, char **argv)
{
int i;
int synchToTimeCode = FALSE;
kern_return_t r;
int synchUnit; /* Serial port to listen for time code */
char *filename = NULL;
MIDIReplyFunctions funcs = {0};
signal (SIGINT, cleanup); /* Control-C routine to clean up gracefully */
while ((i = getopt(argc, argv, "p:f:t:s:")) != EOF)
switch (i) {
case 'p':
unit = ((!strcmp (optarg ,"a") || !strcmp(optarg,"A")) ?
MIDI_PORT_A_UNIT : MIDI_PORT_B_UNIT);
break;
case 'f':
filename = optarg ;
break;
case 't':
tempo = atoi(optarg) ;
fprintf(stderr,"tempo= %d\n",tempo);
break;
case 's':
synchToTimeCode = TRUE;
synchUnit = (!strcmp (optarg ,"a")) ? MIDI_PORT_A_UNIT : MIDI_PORT_B_UNIT;
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_A_UNIT ? "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");
}
s = NXMapFile(filename,NX_READONLY);
if (!s) {
fprintf(stderr,"Cannot open file : %s\n", filename);
exit(1);
}
initFile();
/* 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_CLBSMODE_INTERNAL));
checkForError("MIDISetClockMode",r);
r = MIDISetClockQuantum(driverPort, ownerPort, 1000);
checkForError("MIDISetClockQuantum",r);
ports = createPortSet(queuePort=allocPort(), exceptionPort=allocPort());
r = MIDIRequestExceptions(driverPort, ownerPort, exceptionPort);
checkForError("MIDIRequestExceptions",r);
r = MIDIGetAvailableQueueSize(driverPort, ownerPort, unit, &maxQueueSize);
checkForError("MIDIGetAvailableQueueSize",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);
}
/*
* We play the file by chaining invocations of myQueueReply(). To
* start the process, we ask the driver to invoke myQueueReply() when
* the queue is fully available (which will be immediately, since nothing
* has been sent yet.)
*
* Note: If this code is included in an Application, you must either
* run MIDIAwaitReply() in a separate Mach thread or use MIDIHandleReply()
* instead of MIDIAwaitReply() and register the port set with DPSAddPort().
* See <mididriver/midi_driver.h> for details.
*/
r = MIDIRequestQueueNotification(driverPort, ownerPort, unit, queuePort,
maxQueueSize);
checkForError("MIDIRequestQueueNotification",r);
funcs.exceptionReply = myExceptionReply;
funcs.queueReply = myQueueReply;
if (synchToTimeCode)
fprintf(stderr,"Waiting for time code to start...\n");
allSent = FALSE;
allRead = FALSE;
someSent = TRUE;
while (!allSent) { /* Here's where the work happens */
r = MIDIAwaitReply(ports,&funcs,MIDI_NO_TIMEOUT);
checkForError("MIDIAwaitReply",r);
}
/* Wait for our output to drain. */
r = MIDIRequestQueueNotification(driverPort, ownerPort, unit, queuePort,
maxQueueSize);
checkForError("MIDIRequestQueueNotification",r);
funcs.queueReply = NULL;
r = MIDIAwaitReply(ports,&funcs,MIDI_NO_TIMEOUT);
checkForError("MIDIAwaitReply",r);
cleanup();
}
static MIDIRawEvent events[MIDI_MAX_EVENT];
staBSbyteIndex = 0;
static void *fileStruct; /* Used by MIDIFILE routines. */
static MIDIFILEReadStruct fileEvent = {0};
static int getNextFileByte(void)
{
static int fileEventCtr = 0;
if (fileEventCtr == 0)
if (!MIDIFILEReadEvent(fileStruct))
return FALSE;
if (fileEvent.metaEventFlag)
fileEventCtr = 0;
else {
events[byteIndex].time = fileEvent.quanta;
events[byteIndex].byte = fileEvent.data[fileEventCtr++];
byteIndex++;
if (fileEventCtr == fileEvent.nData)
fileEventCtr = 0;
}
return TRUE;
}
static int sendData(void)
/* Sends the data. Returns FALSE if the driver's buffer is full. */
{
kern_return_t r =
MIDISendData(driverPort, ownerPort, unit, events, byteIndex);
if (r == MIDI_ERROR_QUEUE_FULL) {
/* Request notification when at least half the queue is available */
r = MIDIRequestQueueNotification(driverPort, ownerPort,
unit, queuePort,
maxQueueSize/2);
checkForError("MIDIRequestQueueNotification",r);
return FALSE;
} else checkForError("MIDISendData",r);
byteIndex = 0;
return TRUE;
}
static void myQueueReply(port_t replyPort, short unit)
/* This gets invoked when the queue has enough room for more data. */
{
for (;;) {
if (byteIndex == MIDI_MAX_EVENT-1)
if (!sendData())
return;
if (!allRead)
allRead = !getNextFileByte();
if (allRead) {
if (sendData())
allSent = TRUE;
return;
}
}
}
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 usage(void)
{
fprintf(stderr,
"usage: playmidifile -f file.midi [-p {A, B}] [-s {A, B}]\n"
" -p is the serial port to send the MIDI\n"
" -s is the serial port to receive MIDI time code, if any.\n");
}
static void checkForError(char *msg,int errorReturn)
/* Checks for error. If error, prints message and quits. */
{
if (errorReturn != KERN_SUCCESS) {
switch (errorReturBS
case MIDI_ERROR_BUSY:
printf("%s: %s",msg,"MIDI driver busy.\n");
break;
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 queuePort, port_t exceptionPort)
/* Creates the port set and adds the two ports. */
{
port_set_name_t aPortSet;
int r = port_set_allocate(task_self(), &aPortSet);
checkForError("createPortSet",r);
r = port_set_add(task_self(), aPortSet, queuePort);
checkForError("createPortSet",r);
r = port_set_add(task_self(), aPortSet, exceptionPort);
checkForError("createPortSet",r);
return aPortSet;
}
static void setTempo(int newTempo)
{
MIDIFILESetReadQuantaSize(fileStruct,(tempo = newTempo)/60.0 * 1000);
}
static void initFile(void)
{
int level,trackCount;
fileStruct = MIDIFILEBeginReading(s,&fileEvent);
if (MIDIFILEReadPreamble(fileStruct,&level,&trackCount)) {
if (level != 0) {
fprintf(stderr,"playmidifile cannot play level %d files.\n",level);
exit(1);
}
}
else {
fprintf(stderr,"failed to read preamble!\n");
exit(1);
}
setTempo(tempo);
}
static void allNotesOff(void) {
#define NOTEOFF_ARRAY_SIZE (128*2) /* Use running status */
MIDIRawEvent arr[NOTEOFF_ARRAY_SIZE];
kern_return_t r;
int chan = 0;
int bytesToSend,i;
MIDIRawEvent op;
MIDIReplyFunctions funcs = {0};
for (i=0; i<128; i++) { /* Initialize array */
arr[i*2].byte = i; /* KeyNum */
arr[i*2+1].byte = 0; /* Velocity */
}
while (MIDIAwaitReply(ports,&funcs,1) == KERN_SUCBS)
; /* Empty out ports of any old notification messages. */
r = MIDIClearQueue(driverPort,ownerPort,unit);
checkForError("MIDIClearQueue",r);
for (chan = 0; chan < 16; chan++) {
op.byte = MIDI_NOTEOFF | chan;
r = MIDISendData(driverPort,ownerPort,unit,&op,1);
checkForError("MIDISendData",r);
for (i = 0; i < NOTEOFF_ARRAY_SIZE; i += MIDI_MAX_EVENT) {
bytesToSend = NOTEOFF_ARRAY_SIZE - i;
if (bytesToSend > MIDI_MAX_EVENT)
bytesToSend = MIDI_MAX_EVENT;
r = MIDISendData(driverPort,ownerPort,unit,&arr[i],bytesToSend);
checkForError("MIDISendData",r);
}
r = MIDIFlushQueue(driverPort,ownerPort,unit);
checkForError("MIDIFlushQueue",r);
r = MIDIRequestQueueNotification(driverPort, ownerPort,unit,
queuePort,
((chan==15) ? maxQueueSize :
NOTEOFF_ARRAY_SIZE+1));
checkForError("MIDIRequestQueueNotification",r);
r = MIDIAwaitReply(ports,&funcs,MIDI_NO_TIMEOUT);
checkForError("MIDIAwaitReply",r);
}
}
static void cleanup() {
kern_return_t r;
if (!driverPort || !ownerPort)
exit(0);
if (someSent)
allNotesOff();
r = MIDIReleaseOwnership(driverPort,ownerPort);
checkForError("MIDIReleaseOwnership",r);
exit(0);
}