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;