Hi,

This patch adds three new options to arecordmidi.

       -d,--dump
              Shows the received events as text on standard output.

       -m,--metronome=client:port
              Plays a metronome signal on the specified sequencer port.

              Metronome  sounds  are  played on channel 10, MIDI notes 33 & 34
              (GM metronome standard notes), with velocity 100 and duration 1.

       -i,--timesig=numerator:denominator
              Sets the time signature for the MIDI file and metronome.

              The  time signature is specified as usual with two numbers, rep-
              resenting the numerator and denominator of the time signature as
              it  would  be  notated.  Both  numbers  should be separated by a
              colon. The time signature is 4:4 by default.

Regards,
Pedro
Index: arecordmidi.1
===================================================================
RCS file: /cvsroot/alsa/alsa-utils/seq/aplaymidi/arecordmidi.1,v
retrieving revision 1.1
diff -u -r1.1 arecordmidi.1
--- arecordmidi.1	23 Feb 2004 10:58:35 -0000	1.1
+++ arecordmidi.1	4 Apr 2004 12:49:01 -0000
@@ -58,5 +58,25 @@
 Specifies that the data for each MIDI channel should be written to a
 separate track in the MIDI file.
 
+.TP
+.I -d,--dump
+Shows the events received as text on standard output.
+
+.TP
+.I -m,--metronome=client:port
+Plays a metronome signal on the specified sequencer port.
+
+Metronome sounds are played on channel 10, MIDI notes 33 & 34 (GM 
+metronome standard notes), with velocity 100 and duration 1.
+
+.TP
+.I -i,--timesig=numerator:denominator
+Sets the time signature for the MIDI file and metronome.
+
+The time signature is specified as usual with two numbers, representing
+the numerator and denominator of the time signature as it would be 
+notated. Both numbers should be separated by a colon. The time signature 
+is 4:4 by default.
+
 .SH AUTHOR
 Clemens Ladisch <[EMAIL PROTECTED]>
Index: arecordmidi.c
===================================================================
RCS file: /cvsroot/alsa/alsa-utils/seq/aplaymidi/arecordmidi.c,v
retrieving revision 1.1
diff -u -r1.1 arecordmidi.c
--- arecordmidi.c	23 Feb 2004 10:58:35 -0000	1.1
+++ arecordmidi.c	4 Apr 2004 12:49:02 -0000
@@ -53,6 +53,14 @@
 /* timing/sysex + 16 channels */
 #define TRACKS_PER_PORT 17
 
+/* metronome settings */
+/* TODO: create options for this */
+#define METRONOME_CHANNEL 9
+#define METRONOME_STRONG_NOTE 34
+#define METRONOME_WEAK_NOTE 33
+#define METRONOME_VELOCITY 100
+#define METRONOME_PROGRAM 0
+
 
 static snd_seq_t *seq;
 static int client;
@@ -68,7 +76,16 @@
 static int num_tracks;
 static struct smf_track *tracks;
 static volatile sig_atomic_t stop = 0;
-
+static int dump = 0;
+static snd_seq_addr_t *metronome_port = NULL;
+static int metronome_weak_note = METRONOME_WEAK_NOTE;
+static int metronome_strong_note = METRONOME_STRONG_NOTE;
+static int metronome_velocity = METRONOME_VELOCITY;
+static int metronome_program = METRONOME_PROGRAM;
+static int metronome_channel = METRONOME_CHANNEL;
+static int ts_num = 4; /* time signature: numerator */
+static int ts_div = 4; /* time signature: denominator */
+static int ts_dd = 2; /* time signature: denominator as a power of two */
 
 /* prints an error message to stderr, and dies */
 static void fatal(const char *msg, ...)
@@ -142,6 +159,159 @@
 	free(buf);
 }
 
+/* Metronome port setting */
+static void init_metronome(const char *arg)
+{
+	int err;
+	
+	metronome_port = malloc(sizeof(snd_seq_addr_t));
+	check_mem(metronome_port);
+	err = snd_seq_parse_address(seq, metronome_port, arg);
+	if (err < 0)
+		fatal("Invalid port %s - %s", arg, snd_strerror(err));
+}
+
+/* parses time signature specification */
+static void time_signature(const char *arg)
+{
+	long x = 0;
+	char *sep;
+
+        x = strtol(arg, &sep, 10);
+	if ((x < 1) | (x > 32) | (*sep != ':'))
+		fatal("Invalid time signature (%s)", arg);
+	ts_num = x;
+	x = strtol(++sep, NULL, 10);
+	if ((x < 1) | (x > 32))
+		fatal("Invalid time signature (%s)", arg);
+	ts_div = x;
+	for(ts_dd = 0; x > 1; x /= 2) ts_dd++;
+}
+
+/*
+ * Dump incoming events
+ */
+static void print_syx(int len, unsigned char *data)
+{
+	int i;
+	
+	for (i=0; i<len; i++) {
+		printf("%02x ", data[i]);
+	}
+	printf("\n");
+}
+
+static void print_time(snd_seq_event_t *ev)
+{
+	printf("%11d ", ev->time.tick);
+}
+
+static void print_midi_event(snd_seq_event_t *ev)
+{
+	switch (ev->type) {
+	case SND_SEQ_EVENT_SENSING:
+		print_time(ev);
+		printf("Active Sensing\n");
+		break;
+	case SND_SEQ_EVENT_NOTEOFF:
+		print_time(ev);
+		printf("Note off               %2d %3d %3d\n",
+		ev->data.note.channel, ev->data.note.note, ev->data.note.velocity);
+		break;
+	case SND_SEQ_EVENT_NOTEON:
+		print_time(ev);
+		printf("Note on                %2d %3d %3d\n",
+		ev->data.note.channel, ev->data.note.note, ev->data.note.velocity);
+		break;
+	case SND_SEQ_EVENT_KEYPRESS:
+		print_time(ev);
+		printf("Polyphonic aftertouch  %2d %3d %3d\n",
+		ev->data.note.channel, ev->data.note.note, ev->data.note.velocity);
+		break;
+	case SND_SEQ_EVENT_PITCHBEND:
+		print_time(ev);
+		printf("Pich bender            %2d  %6d\n",
+		ev->data.control.channel, ev->data.control.value);
+		break;
+	case SND_SEQ_EVENT_CHANPRESS:
+		print_time(ev);
+		printf("Channel aftertouch     %2d %3d\n",
+		ev->data.control.channel, ev->data.control.value);
+		break;
+	case SND_SEQ_EVENT_CONTROLLER:
+		print_time(ev);
+		printf("Control change         %2d %3d %3d\n",
+		ev->data.control.channel, ev->data.control.param, ev->data.control.value);
+		break;
+	case SND_SEQ_EVENT_PGMCHANGE:
+		print_time(ev);
+		printf("Program change         %2d %3d\n",
+		ev->data.control.channel, ev->data.control.value);
+		break;
+	case SND_SEQ_EVENT_SYSEX:
+		print_time(ev);
+		printf("System  exclusive      ");
+		print_syx(ev->data.ext.len, ev->data.ext.ptr);
+		break;
+	case SND_SEQ_EVENT_ECHO:
+		break;
+	default:
+		print_time(ev);
+		printf("Event type %d\n",  ev->type);
+	}
+}
+
+/*
+ * Metronome implementation
+ */
+static void metronome_note(unsigned char note, int tick)
+{
+	snd_seq_event_t ev;
+	snd_seq_ev_clear(&ev);
+	snd_seq_ev_set_note(&ev, metronome_channel, note, metronome_velocity, 1);
+	snd_seq_ev_schedule_tick(&ev, queue, 1, tick);
+	snd_seq_ev_set_source(&ev, port_count);
+	snd_seq_ev_set_subs(&ev);
+	snd_seq_event_output(seq, &ev);
+}
+
+static void metronome_echo(int tick)
+{
+	snd_seq_event_t ev;
+	snd_seq_ev_clear(&ev);
+	ev.type = SND_SEQ_EVENT_ECHO;
+	snd_seq_ev_schedule_tick(&ev, queue, 1, tick);
+	snd_seq_ev_set_source(&ev, port_count);
+	snd_seq_ev_set_dest(&ev, client, 0);
+	snd_seq_event_output(seq, &ev);
+}
+
+static void metronome_pattern(void)
+{
+	int j, t, duration;
+
+	t = 0;
+	duration = ticks * 4 / ts_div;
+	for (j = 0; j < ts_num; j++) {
+		metronome_note(j ? metronome_weak_note : metronome_strong_note, t);
+		t += duration;
+	}
+	metronome_echo(t);
+        snd_seq_drain_output(seq);
+}
+
+static void metronome_set_program(void)
+{
+	snd_seq_event_t ev;
+
+	snd_seq_ev_clear(&ev);
+	snd_seq_ev_set_pgmchange(&ev, metronome_channel, metronome_program);
+	snd_seq_ev_set_source(&ev, port_count);
+	snd_seq_ev_set_subs(&ev);
+	snd_seq_event_output(seq, &ev);
+}
+
+
 static void init_tracks(void)
 {
 	int i;
@@ -237,6 +407,18 @@
 		err = snd_seq_create_port(seq, pinfo);
 		check_snd("create port", err);
 	}
+
+	/* create an optional metronome port*/
+	if (metronome_port) {
+		snd_seq_port_info_set_capability(pinfo,
+						 SND_SEQ_PORT_CAP_READ |
+						 SND_SEQ_PORT_CAP_SUBS_READ);
+		snd_seq_port_info_set_port(pinfo, port_count);
+
+		snd_seq_port_info_set_name(pinfo, "arecordmidi metronome");
+		err = snd_seq_create_port(seq, pinfo);
+		check_snd("create metronome port", err);
+	}
 }
 
 static void connect_ports(void)
@@ -249,6 +431,14 @@
 			fatal("Cannot connect from port %d:%d - %s",
 			      ports[i].client, ports[i].port, snd_strerror(err));
 	}
+
+	/* subscribe the metronome port */
+	if (metronome_port) {
+	        err = snd_seq_connect_to(seq, port_count, metronome_port->client, metronome_port->port);
+		if (err < 0)
+	    		fatal("Cannot connect to port %d:%d - %s",
+			      metronome_port->client, metronome_port->port, snd_strerror(err));
+	}
 }
 
 /* records a byte to be written to the .mid file */
@@ -444,6 +634,9 @@
 		for (; i < ev->data.ext.len; ++i)
 			add_byte(track, ((unsigned char*)ev->data.ext.ptr)[i]);
 		break;
+	case SND_SEQ_EVENT_ECHO:
+		metronome_pattern();
+		break;
 	default:
 		return;
 	}
@@ -561,7 +754,10 @@
 		"  -b,--bpm=beats             tempo in beats per minute\n"
 		"  -f,--fps=frames            resolution in frames per second (SMPTE)\n"
 		"  -t,--ticks=ticks           resolution in ticks per beat or frame\n"
-		"  -s,--split-channels        create a track for each channel\n",
+		"  -s,--split-channels        create a track for each channel\n"
+		"  -d,--dump                  dump events on standard output\n"
+		"  -m,--metronome=client:port play a metronome signal\n"
+		"  -i,--timesig=nn:dd         time signature\n",
 		argv0);
 }
 
@@ -577,7 +773,7 @@
 
 int main(int argc, char *argv[])
 {
-	static char short_options[] = "hVlp:b:f:t:s";
+	static char short_options[] = "hVlp:b:f:t:sdm:i:";
 	static struct option long_options[] = {
 		{"help", 0, NULL, 'h'},
 		{"version", 0, NULL, 'V'},
@@ -587,6 +783,9 @@
 		{"fps", 1, NULL, 'f'},
 		{"ticks", 1, NULL, 't'},
 		{"split-channels", 0, NULL, 's'},
+		{"dump", 0, NULL, 'd'},
+		{"metronome", 1, NULL, 'm'},
+		{"timesig", 1, NULL, 'i'},
 		{ }
 	};
 
@@ -634,6 +833,15 @@
 		case 's':
 			channel_split = 1;
 			break;
+		case 'd':
+			dump = 1;
+			break;
+		case 'm':
+			init_metronome(optarg);
+			break;
+		case 'i':
+			time_signature(optarg);
+			break;
 		default:
 			help(argv[0]);
 			return 1;
@@ -679,6 +887,16 @@
 		add_byte(&tracks[0], usecs_per_quarter >> 8);
 		add_byte(&tracks[0], usecs_per_quarter);
 	}
+	/* time signature */
+	var_value(&tracks[0], 0); /* delta time */
+	add_byte(&tracks[0], 0xff);
+	add_byte(&tracks[0], 0x58);
+	var_value(&tracks[0], 4);
+	add_byte(&tracks[0], ts_num);
+	add_byte(&tracks[0], ts_dd);
+	add_byte(&tracks[0], 24);
+	add_byte(&tracks[0], 8);
+	
 	/* always write at least one track */
 	tracks[0].used = 1;
 
@@ -692,6 +910,16 @@
 
 	err = snd_seq_nonblock(seq, 1);
 	check_snd("set nonblock mode", err);
+	
+	if (dump) {
+		printf("Waiting for data. Press Ctrl+C to end\n");
+		printf("_______Tick Event_________________ Ch _Data__\n");
+	}
+	
+	if (metronome_port) {
+		metronome_set_program();
+		metronome_pattern();
+	}
 
 	signal(SIGINT, sighandler);
 	signal(SIGTERM, sighandler);
@@ -709,6 +937,8 @@
 				break;
 			if (event)
 				record_event(event);
+			if (dump)
+				print_midi_event(event);
 		} while (err > 0);
 		if (stop)
 			break;

Reply via email to