ftp.nice.ch/pub/next/unix/music/NewMidiDrv.s.tar.gz#/NewMidiDriver/recordmidifile.c

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.