summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandre Ratchov <ratchov@cvs.openbsd.org>2010-02-13 13:45:30 +0000
committerAlexandre Ratchov <ratchov@cvs.openbsd.org>2010-02-13 13:45:30 +0000
commitc54c85f1f3d0aa3ea8a198fa76a4242391494413 (patch)
treecb41d0408743f814ca0303200bdaa47ee2160a4b
parent39c354b29ceb888467a9dbbeb6de4ac3a0fa8747 (diff)
convert midiplay to sndio(7) so it can be used with soft synths, for
instance. Now, the -f option sets the MIDI device (instead of the sequencer(4) device). The -d and -l options were removed.
-rw-r--r--usr.bin/midiplay/Makefile3
-rw-r--r--usr.bin/midiplay/midiplay.146
-rw-r--r--usr.bin/midiplay/midiplay.c239
3 files changed, 95 insertions, 193 deletions
diff --git a/usr.bin/midiplay/Makefile b/usr.bin/midiplay/Makefile
index c487867a90d..d8f6ac3ea7c 100644
--- a/usr.bin/midiplay/Makefile
+++ b/usr.bin/midiplay/Makefile
@@ -1,7 +1,8 @@
-# $OpenBSD: Makefile,v 1.1 1999/01/01 23:58:22 niklas Exp $
+# $OpenBSD: Makefile,v 1.2 2010/02/13 13:45:29 ratchov Exp $
# $NetBSD: Makefile,v 1.1 1998/08/12 21:39:11 augustss Exp $
# @(#)Makefile 8.1 (Berkeley) 6/6/93
PROG= midiplay
+LDADD+= -lsndio
.include <bsd.prog.mk>
diff --git a/usr.bin/midiplay/midiplay.1 b/usr.bin/midiplay/midiplay.1
index 9dab4231a19..cf5b2ac26d3 100644
--- a/usr.bin/midiplay/midiplay.1
+++ b/usr.bin/midiplay/midiplay.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: midiplay.1,v 1.14 2009/08/16 09:41:08 sobrado Exp $
+.\" $OpenBSD: midiplay.1,v 1.15 2010/02/13 13:45:29 ratchov Exp $
.\" $NetBSD: midiplay.1,v 1.3 1998/08/13 18:26:36 augustss Exp $
.\"
.\" Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: August 16 2009 $
+.Dd $Mdocdate: February 13 2010 $
.Dt MIDIPLAY 1
.Os
.Sh NAME
@@ -35,34 +35,27 @@
.Nd play MIDI files
.Sh SYNOPSIS
.Nm midiplay
-.Op Fl glmqvx
-.Op Fl d Ar devno
-.Op Fl f Ar file
+.Op Fl gmqvx
+.Op Fl f Ar device
.Op Fl t Ar tempo
.Op Ar
.Sh DESCRIPTION
The
.Nm
-command plays MIDI files using the sequencer device.
+command plays MIDI files.
If no file name is given it will play from standard input;
otherwise it will play the named files.
.Pp
The options are as follows:
-.Bl -tag -width "-d devnoXX"
-.It Fl d Ar devno
-Specifies the number of the MIDI device used for output (as listed
-by the
-.Fl l
-flag).
-The default is device 0.
-.It Fl f Ar file
-Specifies the name of the sequencer device.
+.Bl -tag -width Ds
+.It Fl f Ar device
+Specifies the name of the
+.Xr sndio 7
+MIDI device.
.It Fl g
Send a
.Dq General MIDI On
system exclusive message to the device.
-.It Fl l
-List the possible devices without playing anything.
.It Fl m
Show MIDI file meta events (copyright, lyrics, etc.).
.It Fl q
@@ -76,25 +69,12 @@ If the flag is repeated, the verbosity increases.
.It Fl x
Play a small sample sound.
.El
-.Sh FILES
-.Bl -tag -width /dev/music
-.It Pa /dev/music
-MIDI sequencer device
-.El
-.\".Sh ENVIRONMENT
-.\".Bl -tag -width MIDIDEVICE
-.\".It Pa AUDIOCTLDEVICE
-.\"the audio control device to use.
-.\".El
.Sh SEE ALSO
-.Xr midi 4
+.Xr midicat 1 ,
+.Xr midi 4 ,
+.Xr sndio 7
.Sh HISTORY
The
.Nm
command first appeared in
.Nx 1.4 .
-.Sh BUGS
-It may take a long while before playing stops when
-.Nm
-is interrupted since the sequencer and MIDI buffers will still
-be emptied.
diff --git a/usr.bin/midiplay/midiplay.c b/usr.bin/midiplay/midiplay.c
index 46af4c873a2..87e76571041 100644
--- a/usr.bin/midiplay/midiplay.c
+++ b/usr.bin/midiplay/midiplay.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: midiplay.c,v 1.11 2009/10/14 18:22:49 sobrado Exp $ */
+/* $OpenBSD: midiplay.c,v 1.12 2010/02/13 13:45:29 ratchov Exp $ */
/* $NetBSD: midiplay.c,v 1.8 1998/11/25 22:17:07 augustss Exp $ */
/*
@@ -32,9 +32,7 @@
#include <sys/types.h>
#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/midiio.h>
-
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
@@ -42,8 +40,7 @@
#include <err.h>
#include <unistd.h>
#include <string.h>
-
-#define DEVMUSIC "/dev/music"
+#include <sndio.h>
struct track {
u_char *start, *end;
@@ -77,11 +74,15 @@ static int midi_lengths[] = { 2,2,2,2,1,1,2,0 };
/* Number of bytes in a MIDI command */
#define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7])
+#define MIDI_IS_STATUS(d) ((d) & 0x80)
+#define MIDI_IS_COMMON(d) ((d) < 0xf0)
+#define MIDI_SYSEX_START 0xf0
+#define MIDI_SYSEX_STOP 0xf7
+
void usage(void);
-void send_event(seq_event_rec *);
+void send_event(u_char, u_char *, u_int);
void dometa(u_int, u_char *, u_int);
void midireset(void);
-void send_sysex(u_char *, u_int);
u_long getvar(struct track *);
void playfile(FILE *, char *);
void playdata(u_char *, u_int, char *);
@@ -126,30 +127,33 @@ void
usage(void)
{
printf("usage: "
- "%s [-glmqvx] [-d devno] [-f file] [-t tempo] [file ...]\n",
+ "%s [-gmqvx] [-f device] [-t tempo] [file ...]\n",
__progname);
exit(1);
}
int showmeta = 0;
int verbose = 0;
-#define BASETEMPO 400000
-u_int tempo = BASETEMPO; /* microsec / quarter note */
-u_int ttempo = 100;
-int unit = 0;
+u_int tempo = 60 * 1000000 / 100; /* default tempo is 100bpm */
int play = 1;
-int fd;
+struct mio_hdl *hdl;
+struct timespec ts, ts_last;
void
-send_event(seq_event_rec *ev)
+send_event(u_char status, u_char *data, u_int len)
{
- /*
- printf("%02x %02x %02x %02x %02x %02x %02x %02x\n",
- ev->arr[0], ev->arr[1], ev->arr[2], ev->arr[3],
- ev->arr[4], ev->arr[5], ev->arr[6], ev->arr[7]);
- */
- if (play)
- write(fd, ev, sizeof *ev);
+ u_int i;
+
+ if (verbose > 1) {
+ printf("MIDI %02x", status);
+ for (i = 0; i < len; i++)
+ printf(" %02x", data[i]);
+ printf("\n");
+ }
+ if (play) {
+ mio_write(hdl, &status, 1);
+ mio_write(hdl, data, len);
+ }
}
u_long
@@ -213,29 +217,7 @@ midireset(void)
/* General MIDI reset sequence */
static u_char gm_reset[] = { 0x7e, 0x7f, 0x09, 0x01, 0xf7 };
- send_sysex(gm_reset, sizeof gm_reset);
-}
-
-#define SYSEX_CHUNK 6
-void
-send_sysex(u_char *p, u_int l)
-{
- seq_event_rec event;
- u_int n;
-
- event.arr[0] = SEQ_SYSEX;
- event.arr[1] = unit;
- do {
- n = SYSEX_CHUNK;
- if (l < n) {
- memset(&event.arr[2], 0xff, SYSEX_CHUNK);
- n = l;
- }
- memcpy(&event.arr[2], p, n);
- send_event(&event);
- l -= n;
- p += n;
- } while (l > 0);
+ send_event(MIDI_SYSEX_START, gm_reset, sizeof gm_reset);
}
void
@@ -275,15 +257,21 @@ playfile(FILE *f, char *name)
}
void
+sigalrm(int i)
+{
+}
+
+void
playdata(u_char *buf, u_int tot, char *name)
{
+ long long delta_nsec = 0;
+ u_int delta_ticks;
int format, ntrks, divfmt, ticks, t, besttrk = 0;
- u_int len, mlen, status, chan;
- u_char *p, *end, byte, meta, *msg;
+ u_int len, mlen;
+ u_char *p, *end, byte, meta;
struct track *tracks;
u_long bestcur, now;
struct track *tp;
- seq_event_rec event;
end = buf + tot;
if (verbose)
@@ -342,25 +330,10 @@ playdata(u_char *buf, u_int tot, char *name)
* curtime. Execute the event, update the curtime and repeat.
*/
- /*
- * The ticks variable is the number of ticks that make up a quarter
- * note and is used as a reference value for the delays between
- * the MIDI events.
- * The sequencer has two "knobs": the TIMEBASE and the TEMPO.
- * The delay specified in TMR_WAIT_REL is specified in
- * sequencer time units. The length of a unit is
- * 60*1000000 / (TIMEBASE * TEMPO).
- * Set it to 1ms/unit (adjusted by user tempo changes).
- */
- t = 500 * ttempo / 100;
- if (ioctl(fd, SEQUENCER_TMR_TIMEBASE, &t) < 0)
- err(1, "SEQUENCER_TMR_TIMEBASE");
- t = 120;
- if (ioctl(fd, SEQUENCER_TMR_TEMPO, &t) < 0)
- err(1, "SEQUENCER_TMR_TEMPO");
- if (ioctl(fd, SEQUENCER_TMR_START, 0) < 0)
- err(1, "SEQUENCER_TMR_START");
now = 0;
+ delta_nsec = 0;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts_last) < 0)
+ err(1, "clock_gettime");
for (;;) {
/* Locate lowest curtime */
bestcur = ~0;
@@ -376,25 +349,19 @@ playdata(u_char *buf, u_int tot, char *name)
printf("DELAY %4ld TRACK %2d ", bestcur-now, besttrk);
fflush(stdout);
}
- if (now < bestcur) {
- union {
- u_int32_t i;
- u_int8_t b[4];
- } u;
- u_int32_t delta = bestcur - now;
- delta = (int)((double)delta * tempo / (1000.0*ticks));
- u.i = delta;
- if (delta != 0) {
- event.arr[0] = SEQ_TIMING;
- event.arr[1] = TMR_WAIT_REL;
- event.arr[4] = u.b[0];
- event.arr[5] = u.b[1];
- event.arr[6] = u.b[2];
- event.arr[7] = u.b[3];
- send_event(&event);
- }
+ while (now < bestcur) {
+ pause();
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
+ err(1, "clock_gettime");
+ delta_nsec += 1000000000L * (ts.tv_sec - ts_last.tv_sec);
+ delta_nsec += ts.tv_nsec - ts_last.tv_nsec;
+ ts_last = ts;
+ if (delta_nsec <= 0)
+ continue;
+ delta_ticks = delta_nsec * ticks / (1000LL * tempo);
+ delta_nsec -= 1000LL * delta_ticks * tempo / ticks;
+ now += delta_ticks;
}
- now = bestcur;
tp = &tracks[besttrk];
byte = *tp->start++;
if (byte == MIDI_META) {
@@ -409,52 +376,16 @@ playdata(u_char *buf, u_int tot, char *name)
tp->status = byte;
else
tp->start--;
- mlen = MIDI_LENGTH(tp->status);
- msg = tp->start;
- if (verbose > 1) {
- if (mlen == 1)
- printf("MIDI %02x (%d) %02x\n",
- tp->status, mlen, msg[0]);
- else
- printf("MIDI %02x (%d) %02x %02x\n",
- tp->status, mlen, msg[0], msg[1]);
- }
- status = MIDI_GET_STATUS(tp->status);
- chan = MIDI_GET_CHAN(tp->status);
- switch (status) {
- case MIDI_NOTEOFF:
- case MIDI_NOTEON:
- case MIDI_KEY_PRESSURE:
- SEQ_MK_CHN_VOICE(&event, unit, status, chan,
- msg[0], msg[1]);
- send_event(&event);
- break;
- case MIDI_CTL_CHANGE:
- SEQ_MK_CHN_COMMON(&event, unit, status, chan,
- msg[0], 0, msg[1]);
- send_event(&event);
- break;
- case MIDI_PGM_CHANGE:
- case MIDI_CHN_PRESSURE:
- SEQ_MK_CHN_COMMON(&event, unit, status, chan,
- msg[0], 0, 0);
- send_event(&event);
- break;
- case MIDI_PITCH_BEND:
- SEQ_MK_CHN_COMMON(&event, unit, status, chan,
- 0, 0,
- (msg[0] & 0x7f) |
- ((msg[1] & 0x7f) << 7));
- send_event(&event);
- break;
- case MIDI_SYSTEM_PREFIX:
+ if (MIDI_IS_COMMON(tp->status)) {
+ mlen = MIDI_LENGTH(tp->status);
+ send_event(tp->status, tp->start, mlen);
+ } else if (tp->status == MIDI_SYSEX_START) {
+ mlen = getvar(tp);
+ send_event(MIDI_SYSEX_START, tp->start, mlen);
+ } else if (tp->status == MIDI_SYSEX_STOP) {
mlen = getvar(tp);
- if (tp->status == MIDI_SYSEX_START)
- send_sysex(tp->start, mlen);
- else
- /* Sorry, can't do this yet */;
- break;
- default:
+ /* Sorry, can't do this yet */;
+ } else {
if (verbose)
printf("MIDI event 0x%02x ignored\n",
tp->status);
@@ -466,9 +397,6 @@ playdata(u_char *buf, u_int tot, char *name)
else
tp->curtime += getvar(tp);
}
- if (ioctl(fd, SEQUENCER_SYNC, 0) < 0)
- err(1, "SEQUENCER_SYNC");
-
ret:
free(tracks);
}
@@ -477,31 +405,22 @@ int
main(int argc, char **argv)
{
int ch;
- int listdevs = 0;
int example = 0;
int gmreset = 0;
- int nmidi;
- char *file = DEVMUSIC;
- struct synth_info info;
+ char *file = NULL;
FILE *f;
const char *errstr;
+ struct sigaction sa;
+ struct itimerval it;
while ((ch = getopt(argc, argv, "?d:f:glmqt:vx")) != -1) {
switch (ch) {
- case 'd':
- unit = strtonum(optarg, 0, INT_MAX, &errstr);
- if (errstr)
- errx(1, "unit is %s: %s", errstr, optarg);
- break;
case 'f':
file = optarg;
break;
case 'g':
gmreset++;
break;
- case 'l':
- listdevs++;
- break;
case 'm':
showmeta++;
break;
@@ -509,7 +428,8 @@ main(int argc, char **argv)
play = 0;
break;
case 't':
- ttempo = strtonum(optarg, 0, INT_MAX, &errstr);
+ tempo = 60 * 1000000 /
+ strtonum(optarg, 40, 240, &errstr);
if (errstr)
errx(1, "tempo is %s: %s", errstr, optarg);
break;
@@ -527,23 +447,24 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
- fd = open(file, O_WRONLY);
- if (fd < 0)
- err(1, "%s", file);
- if (ioctl(fd, SEQUENCER_NRMIDIS, &nmidi) < 0)
- err(1, "ioctl(SEQUENCER_NRMIDIS) failed, ");
- if (nmidi == 0)
- errx(1, "Sorry, no MIDI devices available");
- if (listdevs) {
- for (info.device = 0; info.device < nmidi; info.device++) {
- if (ioctl(fd, SEQUENCER_INFO, &info) < 0)
- err(1, "ioctl(SEQUENCER_INFO) failed, ");
- printf("%d: %s\n", info.device, info.name);
- }
- exit(0);
+ hdl = mio_open(file, MIO_OUT, 0);
+ if (hdl == NULL) {
+ fprintf(stderr, "failed to open MIDI output\n");
+ exit(1);
}
if (gmreset)
midireset();
+
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = sigalrm;
+ sigfillset(&sa.sa_mask);
+ if (sigaction(SIGALRM, &sa, NULL) < 0)
+ err(1, "sigaction");
+ it.it_interval.tv_sec = it.it_value.tv_sec = 0;
+ it.it_interval.tv_usec = it.it_value.tv_usec = 1000;
+ if (setitimer(ITIMER_REAL, &it, NULL) < 0)
+ err(1, "setitimer");
+
if (example)
playdata(sample, sizeof sample, "<Gubben Noa>");
else if (argc == 0)