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.