home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / SoundAndMusic / Drivers / MidiDriver / recordmidifile.c < prev   
Text File  |  1992-04-24  |  11KB  |  339 lines

  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.  * RewritteBSr 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_BSIT ? "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 MIDIHaBSReply()
  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(byBSbyteIndex+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.         breaBR`         case MIDI_ERROR_NOT_OWNER:
  273.         printf("%s: %s",msg,"You must be owner of the MIDI driver.\n");
  274.         break;
  275.           case MIDI_ERROR_QUEUE_FULL:
  276.         printf("%s: %s",msg,"MIDI driver queue full.\n");
  277.         break;
  278.           case MIDI_ERROR_BAD_MODE:
  279.         printf("%s: %s",msg,"Bad MIDI driver clock mode.\n");
  280.         break;
  281.           case MIDI_ERROR_UNIT_UNAVAILABLE:
  282.         printf("%s: %s",msg,"MIDI driver unit unavailable.\n");
  283.         break;
  284.           case MIDI_ERROR_ILLEGAL_OPERATION:
  285.         printf("%s: %s",msg,"MIDI driver illegal operation.\n");
  286.         break;
  287.       default: 
  288.         mach_error(msg,errorReturn);
  289.     }
  290.     exit(1);
  291.     }
  292. }
  293.  
  294. static port_t allocPort(void)
  295.     /* Allocates a port and returns it. */
  296. {
  297.     port_t aPort;
  298.     int r = port_allocate(task_self(), &aPort);
  299.     checkForError("allocPort",r);
  300.     return aPort;
  301. }
  302.  
  303. static port_t createPortSet(port_t dataPort, port_t exceptionPort,
  304.                 port_t alarmPort) 
  305.     /* Creates the port set and adds the three ports.  */
  306. {
  307.     port_set_name_t aPortSet;
  308.     int r = port_set_allocate(task_self(), &aPortSet);
  309.     checkForError("createPortSet",r);
  310.     r = port_set_add(task_self(), aPortSet, dataPort);
  311.     checkForError("createPortSet",r);
  312.     r = port_set_add(task_self(), aPortSet, alarmPort);
  313.     checkForError("createPortSet",r);
  314.     r = port_set_add(task_self(), aPortSet, exceptionPort);
  315.     checkForError("createPortSet",r);
  316.     return aPortSet;
  317. }
  318.  
  319. static void initFile(int fd)
  320.     /* Set up file. */
  321. {
  322.     s = NXOpenFile(fd,NX_WRITEONLY);
  323.     fileStruct = MIDIFILEBeginWriting(s,0,NULL);
  324.     MIDIFILEWriteTempo(fileStruct,0,60);
  325.     MIDIFILEBeginWritingTrack(fileStruct,NULL);
  326. }
  327.  
  328. void cleanup()
  329.     /* we kill this test program by control-C; must save midifile */
  330. {
  331.     fprintf(stderr,"Received %d MIDI bytes\n",byteCount);    
  332.     MIDIReleaseOwnership(driverPort,ownerPort);
  333.     MIDIFILEEndWritingTrack(fileStruct,0);
  334.     MIDIFILEEndWriting(fileStruct);
  335.     close(fd);
  336.     exit(0);
  337. }
  338.  
  339.