This is recordmidifile.c in view mode; [Download] [Up]
/* * This example test program creates a standard Level 0 Midifile (.midi suffix) * as defined by the Midi Manufacturer's Association, from data received * through the MIDI Driver. * * The MIDI Driver is only the second commercial MACH driver ever written, * and Gregg Kellogg wrote it. Gregg also wrote the test program * this was based on. Lee Boynton wrote the Midifile parsing routines. * Dana Massie combined the two 6-89. Brian Willoughby improved the standard * Midifile support by fixing bugs which created files which weren't true * Level 0 and improving/completing the Midifile parsing routines 2-92. */ #import <mach.h> #import <stdio.h> #import <stdlib.h> #import <mach_error.h> #import <servers/netname.h> #import <signal.h> #import <sys/file.h> #import <strings.h> #import <streams/streams.h> /* Contains NXStream, etc. */ #import "midifile.h" #import <midi/midi_server.h> #import <midi/midi_reply_handler.h> #import <midi/midi_timer.h> #import <midi/midi_timer_reply_handler.h> #import <midi/midi_error.h> #import <midi/midi_timer_error.h> int open(); /* Not declared anywhere else. */ /* * These routines should be prototyped someplace in /usr/include! */ int getopt(int argc, char **argv, char *optstring); int write(int fd, char *data, int size); void usage(void); port_t dev_port; port_t owner_port; port_t timer_port; port_t timer_reply_port; port_t recv_port; port_t recv_reply_port; port_t xmit_port; port_t neg_port; port_set_name_t port_set; int secs = 1; int verbose; kern_return_t my_timer_event ( void *arg, timeval_t timeval, u_int quanta, u_int usec_per_quantum, u_int real_usec_per_quantum, boolean_t timer_expired, boolean_t timer_stopped, boolean_t timer_forward); midi_timer_reply_t midi_timer_reply = { my_timer_event, 0, 0 }; kern_return_t my_ret_raw_data( void * arg, midi_raw_t midi_raw_data, u_int midi_raw_dataCnt); kern_return_t my_ret_cooked_data( void * arg, midi_cooked_t midi_cooked_data, u_int midi_cooked_dataCnt); kern_return_t my_ret_packed_data( void * arg, u_int quanta, midi_packed_t midi_packed_data, u_int midi_packed_dataCnt); midi_reply_t midi_reply = { my_ret_raw_data, my_ret_cooked_data, my_ret_packed_data, 0, 0, 0 }; NXStream *aStream; // output to write midiphile to ... midievent_t theEvent; int fd; #define A_MIDI_PORT "midi0" #define B_MIDI_PORT "midi1" #define DEFAULT_MIDI_PORT A_MIDI_PORT // we kill this test program by control-C; must save midifile void myhandler() { // shut down the read from midi would be nice here // bdw: bug - the following call resulted in a Midifile with more // than one End of Track marker //MIDIFileEndWritingTrack(aStream); MIDIFileEndWriting(aStream); exit(0); } struct sigvec vec = {myhandler, 0, SIGINT}; main(int argc, char **argv) { int i; kern_return_t r; msg_header_t *in_msg; extern char *optarg; extern int optind; char * midiPort, * filename = NULL; // file to read from sigvec(SIGINT, &vec, 0); midiPort = DEFAULT_MIDI_PORT; while ((i = getopt(argc, argv, "p:f:")) != EOF) { switch (i) { case 'p': if ((!strcmp(optarg , "b")) || (!strcmp(optarg, "B"))) midiPort = B_MIDI_PORT; else midiPort = A_MIDI_PORT; break; case 'f': filename = optarg ; break; case 'h': case '?': default: usage(); exit(1); } } if (filename == NULL) { fprintf(stderr, "this guy needs a filename specified...\n"); usage(); exit(1); } fprintf(stderr, "using midi port: %c\n", (A_MIDI_PORT == midiPort) ? 'A' : ((B_MIDI_PORT == midiPort) ? 'B' : '?')); if ((fd = open(filename, O_WRONLY | O_CREAT, 0777)) < 0) { fprintf(stderr, "open failed on filename %s\n", filename); exit(1); } aStream = NXOpenFile(fd, NX_WRITEONLY); // this calls MIDIFileBeginWritingTrack() MIDIFileBeginWriting(aStream, 0, filename); MIDIFileWriteTempo(aStream, 120); // bdw: bug - the following call resulted in a Level 0 Midifile with more // than one track, which is not allowed by the MMA's specification // MIDIFileBeginWritingTrack(aStream, NULL); /* * Get a connection to the midi driver. */ r = netname_look_up(name_server_port, "", midiPort, &dev_port); if (r != KERN_SUCCESS) { mach_error("timer_track: netname_look_up error", r); exit(1); } /* * Become owner of the device. */ r = port_allocate(task_self(), &owner_port); if (r != KERN_SUCCESS) { mach_error("allocate owner port", r); exit(1); } neg_port = PORT_NULL; r = midi_set_owner(dev_port, owner_port, &neg_port); if (r != KERN_SUCCESS) { midi_error("become owner", r); exit(1); } /* * Get the timer port for the device. */ r = midi_get_out_timer_port(dev_port, &timer_port); if (r != KERN_SUCCESS) { midi_error("output timer port", r); exit(1); } /* * Get the receive port for the device. */ r = midi_get_recv(dev_port, owner_port, &recv_port); if (r != KERN_SUCCESS) { midi_error("recv port", r); exit(1); } r = port_allocate(task_self(), &timer_reply_port); if (r != KERN_SUCCESS) { mach_error("allocate timer reply port", r); exit(1); } /* bdw: this comment makes no sense... * Find out what time it is (and other vital information). */ r = port_allocate(task_self(), &recv_reply_port); if (r != KERN_SUCCESS) { mach_error("allocate recv reply port", r); exit(1); } /* * Tell it to ignore system messages we're not interested in. */ r = midi_set_sys_ignores(recv_port, ( MIDI_IGNORE_ACTIVE_SENS | MIDI_IGNORE_TIMING_CLCK | MIDI_IGNORE_START | MIDI_IGNORE_CONTINUE | MIDI_IGNORE_STOP | MIDI_IGNORE_SONG_POS_P)); if (r != KERN_SUCCESS) { mach_error("midi_set_sys_ignores", r); exit(1); } /* bdw: with msg_frame_quanta at 2, * the value of 10 for inter_msg_quanta is meaningless... * Set the protocol to indicate our preferences. */ r = midi_set_proto(recv_port, MIDI_PROTO_COOKED, // cooked FALSE, // NO means absolute time codes wanted MIDI_PROTO_SYNC_SYS,// use system clock 10, // 10 clocks before data sent (bdw: see above) 2, // 2 clock timeout between input chars 8192); // maximum output queue size if (r != KERN_SUCCESS) { mach_error("midi_set_proto", r); exit(1); } /* * Get it to send us received data. */ r = midi_get_data(recv_port, recv_reply_port); if (r != KERN_SUCCESS) { midi_timer_error("midi_get_data", r); exit(1); } /* * Allocate port set. */ r = port_set_allocate(task_self(), &port_set); if (r != KERN_SUCCESS) { mach_error("allocate port set", r); exit(1); } /* * Add timer receive port to port set. */ r = port_set_add(task_self(), port_set, timer_reply_port); if (r != KERN_SUCCESS) { mach_error("add timer_reply_port to set", r); exit(1); } /* * Add data receive port to port set. */ r = port_set_add(task_self(), port_set, recv_reply_port); if (r != KERN_SUCCESS) { mach_error("add recv_reply_port to set", r); exit(1); } /* * Start the timer up. */ r = timer_start(timer_port, owner_port); if (r != KERN_SUCCESS) { midi_error("timer start", r); exit(1); } // bdw: this was previously called before the timer was started // the result was that the first event did not occur at the time requested. // However, this call is not really necessary, since it merely sets up for // display of the current time. MIDI data will be recorded without this. r = timer_quanta_req( timer_port, timer_reply_port, 0, // 0 quanta TRUE); // from now if (r != KERN_SUCCESS) { midi_timer_error("request timer", r); exit(1); } /* * Enter the timer loop. */ in_msg = (msg_header_t *)malloc(MSG_SIZE_MAX); while (1) { in_msg->msg_size = MSG_SIZE_MAX; in_msg->msg_local_port = port_set; r = msg_receive(in_msg, MSG_OPTION_NONE, 0); if (r != KERN_SUCCESS) { mach_error("msg_receive", r); exit(1); } if (in_msg->msg_local_port == recv_reply_port) r = midi_reply_handler(in_msg, &midi_reply); else if (in_msg->msg_local_port == timer_reply_port) r = midi_timer_reply_handler(in_msg, &midi_timer_reply); else { fprintf(stderr, "unknown port\n"); r = KERN_FAILURE; } if (r != KERN_SUCCESS) mach_error("midi_timer_reply_server", r); } } void usage(void) { fprintf(stderr, "usage: recordmidifile -f file.midi -p {a, b} (midi port)\n"); } kern_return_t my_timer_event ( void *arg, timeval_t timeval, u_int quanta, u_int usec_per_quantum, u_int real_usec_per_quantum, boolean_t timer_expired, boolean_t timer_stopped, boolean_t timer_forward) { kern_return_t r; static int nquanta; nquanta += secs * 1000000 / usec_per_quantum; fprintf(stderr, "time is %d usec/quantum %d\n", quanta, real_usec_per_quantum); if (!timer_expired) { fprintf(stderr, "timer hasn't expired\n"); return KERN_SUCCESS; } r = timer_quanta_req(timer_port, timer_reply_port, nquanta, // secs seconds from FALSE); // from timer base if (r != KERN_SUCCESS) { midi_timer_error("request timer", r); exit(1); } return KERN_SUCCESS; } // bdw: This should not be called... kern_return_t my_ret_raw_data( void * arg, midi_raw_t midi_raw_data, u_int midi_raw_dataCnt) { if (verbose) { while (midi_raw_dataCnt--) { fprintf(stderr, "0x%x@%d ", midi_raw_data->data, midi_raw_data->quanta); midi_raw_data++; } fprintf(stderr, "\n"); } else write(1, (char *)midi_raw_data, midi_raw_dataCnt*sizeof(*midi_raw_data)); midi_get_data(recv_port, recv_reply_port); } kern_return_t my_ret_cooked_data( void * arg, midi_cooked_t midi_cooked_data, u_int midi_cooked_dataCnt) { int i; while (midi_cooked_dataCnt--) { theEvent.quanta = midi_cooked_data->quanta; theEvent.ndata = midi_cooked_data->ndata; for (i = 0; i < midi_cooked_data->ndata; i++) theEvent.data[i] = midi_cooked_data->data[i]; theEvent.metaevent = 0; midi_cooked_data++; MIDIFileWriteEvent(aStream, &theEvent); } midi_get_data(recv_port, recv_reply_port); } // bdw: This should not be called... kern_return_t my_ret_packed_data( void * arg, u_int quanta, midi_packed_t midi_packed_data, u_int midi_packed_dataCnt) { if (verbose) { fprintf(stderr, "("); while (midi_packed_dataCnt--) fprintf(stderr, "0x%x:", *midi_packed_data++); fprintf(stderr, ")@%d\n", quanta); } else write(1, (char *)midi_packed_data, midi_packed_dataCnt*sizeof(*midi_packed_data)); midi_get_data(recv_port, recv_reply_port); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.