diff options
author | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2010-02-13 13:45:30 +0000 |
---|---|---|
committer | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2010-02-13 13:45:30 +0000 |
commit | c54c85f1f3d0aa3ea8a198fa76a4242391494413 (patch) | |
tree | cb41d0408743f814ca0303200bdaa47ee2160a4b | |
parent | 39c354b29ceb888467a9dbbeb6de4ac3a0fa8747 (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/Makefile | 3 | ||||
-rw-r--r-- | usr.bin/midiplay/midiplay.1 | 46 | ||||
-rw-r--r-- | usr.bin/midiplay/midiplay.c | 239 |
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) |