File:  [NeXTSTEP 3.3 examples] / Examples / SoundAndMusic / MidiDriver / recordmidifile.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:48:25 2018 UTC (8 years, 1 month ago) by root
Branches: NeXT, MAIN
CVS tags: NeXTSTEP33, HEAD
Sample Programs from NeXSTEP 3.3

/*
 * 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.
 * 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 <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_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");
    }

    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 MIDIHandleReply()
     * 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(bytes,byteIndex+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");
	    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 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);
}


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.