summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/midi/sequencer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound/midi/sequencer.c')
-rw-r--r--sys/dev/sound/midi/sequencer.c2040
1 files changed, 2040 insertions, 0 deletions
diff --git a/sys/dev/sound/midi/sequencer.c b/sys/dev/sound/midi/sequencer.c
new file mode 100644
index 0000000..ba6a21d
--- /dev/null
+++ b/sys/dev/sound/midi/sequencer.c
@@ -0,0 +1,2040 @@
+/*
+ * The sequencer personality manager.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * This is the newmidi sequencer driver. This driver handles io against
+ * /dev/sequencer, midi input and output event queues and event transmittion
+ * to and from a midi device or synthesizer.
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/sound/midi/midi.h>
+#include <dev/sound/midi/sequencer.h>
+
+#ifndef DDB
+#define DDB(x)
+#endif /* DDB */
+
+#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
+ synthesizer and MIDI output) */
+#define SND_DEV_MIDIN 2 /* Raw midi access */
+#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
+
+#define MIDIDEV_MODE 0x2000
+
+/* Length of a sequencer event. */
+#define EV_SZ 8
+#define IEV_SZ 8
+
+/* Return value from seq_playevent and the helpers. */
+enum {
+ MORE,
+ TIMERARMED,
+ QUEUEFULL
+};
+
+/*
+ * These functions goes into seq_op_desc to get called
+ * from sound.c.
+ */
+
+static midi_intr_t seq_intr;
+static seq_callback_t seq_callback;
+
+/* These are the entries to the sequencer driver. */
+static d_open_t seq_open;
+static d_close_t seq_close;
+static d_ioctl_t seq_ioctl;
+static d_read_t seq_read;
+static d_write_t seq_write;
+static d_poll_t seq_poll;
+
+/*
+ * This is the device descriptor for the midi sequencer.
+ */
+seqdev_info seq_op_desc = {
+ "midi sequencer",
+
+ 0,
+
+ seq_open,
+ seq_close,
+ seq_read,
+ seq_write,
+ seq_ioctl,
+ seq_poll,
+
+ seq_callback,
+
+ SEQ_BUFFSIZE, /* Queue Length */
+
+ 0, /* XXX This is not an *audio* device! */
+};
+
+/* Here is the parameter structure per a device. */
+struct seq_softc {
+ seqdev_info *devinfo; /* sequencer device information */
+
+ int fflags; /* Access mode */
+
+ u_long seq_time; /* The beggining time of this sequence */
+ u_long prev_event_time; /* The time of the previous event output */
+ u_long prev_input_time; /* The time of the previous event input */
+ u_long prev_wakeup_time; /* The time of the previous wakeup */
+ struct callout_handle timeout_ch; /* Timer callout handler */
+ long timer_current; /* Current timer value */
+ int timer_running; /* State of timer */
+ int midi_open[NMIDI_MAX]; /* State of midi devices. */
+ int pending_timer; /* Timer change operation */
+ int output_threshould; /* Sequence output threshould */
+ int pre_event_timeout; /* Time to wait event input */
+ int queueout_pending; /* Pending for the output queue */
+ snd_sync_parm sync_parm; /* AIOSYNC parameter set */
+ struct proc *sync_proc; /* AIOSYNCing process */
+};
+
+typedef struct seq_softc *sc_p;
+
+static d_open_t seqopen;
+static d_close_t seqclose;
+static d_ioctl_t seqioctl;
+static d_read_t seqread;
+static d_write_t seqwrite;
+static d_poll_t seqpoll;
+
+#define CDEV_MAJOR SEQ_CDEV_MAJOR
+static struct cdevsw seq_cdevsw = {
+ /* open */ seqopen,
+ /* close */ seqclose,
+ /* read */ seqread,
+ /* write */ seqwrite,
+ /* ioctl */ seqioctl,
+ /* poll */ seqpoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "midi", /* XXX */
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+ /* bmaj */ -1
+};
+
+seqdev_info seq_info[NSEQ_MAX] ;
+static int seq_info_inited;
+u_long nseq = NSEQ_MAX; /* total number of sequencers */
+
+/* The followings are the local function. */
+static int seq_init(void);
+static int seq_queue(sc_p scp, u_char *note);
+static void seq_startplay(sc_p scp);
+static int seq_playevent(sc_p scp, u_char *event);
+static u_long seq_gettime(void);
+static int seq_requesttimer(sc_p scp, int delay);
+static void seq_stoptimer(sc_p scp);
+static void seq_midiinput(sc_p scp, mididev_info *md);
+static int seq_copytoinput(sc_p scp, u_char *event, int len);
+static int seq_extended(sc_p scp, u_char *event);
+static int seq_chnvoice(sc_p scp, u_char *event);
+static int seq_findvoice(mididev_info *md, int chn, int note);
+static int seq_allocvoice(mididev_info *md, int chn, int note);
+static int seq_chncommon(sc_p scp, u_char *event);
+static int seq_timing(sc_p scp, u_char *event);
+static int seq_local(sc_p scp, u_char *event);
+static int seq_sysex(sc_p scp, u_char *event);
+static void seq_timer(void *arg);
+static int seq_reset(sc_p scp);
+static int seq_openmidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p);
+static int seq_closemidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p);
+static void seq_panic(sc_p scp);
+static int seq_sync(sc_p scp);
+
+static seqdev_info * get_seqdev_info(dev_t i_dev, int *unit);
+
+/*
+ * Here are the main functions to interact to the user process.
+ * These are called from snd* functions in sys/i386/isa/snd/sound.c.
+ */
+
+static int
+seq_init(void)
+{
+ int unit;
+ sc_p scp;
+ seqdev_info *devinfo;
+
+ DEB(printf("seq: initing.\n"));
+
+ /* Have we already inited? */
+ if (seq_info_inited)
+ return (1);
+
+ for (unit = 0 ; unit < nseq ; unit++) {
+ /* Allocate the softc. */
+ scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT);
+ if (scp == (sc_p)NULL) {
+ printf("seq%d: softc allocation failed.\n", unit);
+ return (1);
+ }
+ bzero(scp, sizeof(*scp));
+
+ /* Fill the softc and the seq_info for this unit. */
+ scp->seq_time = seq_gettime();
+ scp->prev_event_time = 0;
+ scp->prev_input_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+ callout_handle_init(&scp->timeout_ch);
+ scp->timer_current = 0;
+ scp->timer_running = 0;
+ scp->queueout_pending = 0;
+
+ scp->devinfo = devinfo = &seq_info[unit];
+ bcopy(&seq_op_desc, devinfo, sizeof(seq_op_desc));
+ devinfo->unit = unit;
+ devinfo->softc = scp;
+ devinfo->flags = 0;
+ devinfo->midi_dbuf_in.unit_size = devinfo->midi_dbuf_out.unit_size = EV_SZ;
+ midibuf_init(&devinfo->midi_dbuf_in);
+ midibuf_init(&devinfo->midi_dbuf_out);
+
+ make_dev(&seq_cdevsw, MIDIMKMINOR(unit, SND_DEV_SEQ),
+ UID_ROOT, GID_WHEEL, 0666, "sequencer%d", unit);
+ }
+
+ /* We have inited. */
+ seq_info_inited = 1;
+
+ if (nseq == 1)
+ printf("seq0: Midi sequencer.\n");
+ else
+ printf("seq0-%lu: Midi sequencers.\n", nseq - 1);
+
+ DEB(printf("seq: inited.\n"));
+
+ return (0);
+}
+
+int
+seq_open(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ int unit, s, midiunit;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: opening.\n", unit));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_open: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_open: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ s = splmidi();
+ /* Mark this device busy. */
+ if ((sd->flags & SEQ_F_BUSY) != 0) {
+ splx(s);
+ DEB(printf("seq_open: unit %d is busy.\n", unit));
+ return (EBUSY);
+ }
+ sd->flags |= SEQ_F_BUSY;
+ sd->flags &= ~(SEQ_F_READING | SEQ_F_WRITING);
+ scp->fflags = flags;
+
+ /* Init the queue. */
+ midibuf_init(&sd->midi_dbuf_in);
+ midibuf_init(&sd->midi_dbuf_out);
+
+ /* Init timestamp. */
+ scp->seq_time = seq_gettime();
+ scp->prev_event_time = 0;
+ scp->prev_input_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+
+ splx(s);
+
+ /* Open midi devices. */
+ for (midiunit = 0 ; midiunit < nmidi + nsynth ; midiunit++) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md))
+ seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, p);
+ }
+
+ DEB(printf("seq%d: opened.\n", unit));
+
+ return (0);
+}
+
+int
+seq_close(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ int unit, s, i;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: closing.\n", unit));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_close: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_close: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ s = splmidi();
+
+ if (!(sd->flags & MIDI_F_NBIO))
+ seq_sync(scp);
+
+ /* Stop the timer. */
+ seq_stoptimer(scp);
+
+ /* Reset the sequencer. */
+ seq_reset(scp);
+ seq_sync(scp);
+
+ /* Clean up the midi device. */
+ for (i = 0 ; i < nmidi + nsynth ; i++) {
+ md = &midi_info[i];
+ if (MIDICONFED(md))
+ seq_closemidi(scp, md, scp->fflags, MIDIDEV_MODE, p);
+ }
+
+ /* Stop playing and unmark this device busy. */
+ sd->flags &= ~(SEQ_F_BUSY | SEQ_F_READING | SEQ_F_WRITING);
+
+ splx(s);
+
+ DEB(printf("seq%d: closed.\n", unit));
+
+ return (0);
+}
+
+int
+seq_read(dev_t i_dev, struct uio *buf, int flag)
+{
+ int unit, ret, s, len;
+ sc_p scp;
+ seqdev_info *sd;
+
+ unit = MIDIUNIT(i_dev);
+
+ /*DEB(printf("seq%d: reading.\n", unit));*/
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_read: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_read: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+ if ((scp->fflags & FREAD) == 0) {
+ DEB(printf("seq_read: unit %d is not for reading.\n", unit));
+ return (EIO);
+ }
+
+ s = splmidi();
+
+ /* Begin recording. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_RD);
+
+ /* Have we got the data to read? */
+ if ((sd->flags & SEQ_F_NBIO) != 0 && sd->midi_dbuf_in.rl == 0)
+ ret = EAGAIN;
+ else {
+ len = buf->uio_resid;
+ ret = midibuf_uioread(&sd->midi_dbuf_in, buf, len);
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+ }
+ splx(s);
+
+ return (ret);
+}
+
+int
+seq_write(dev_t i_dev, struct uio *buf, int flag)
+{
+ u_char event[EV_SZ], ev_code;
+ int unit, count, countorg, midiunit, ev_size, p, ret, s;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ unit = MIDIUNIT(i_dev);
+
+ /*DEB(printf("seq%d: writing.\n", unit));*/
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_write: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_write: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+ if ((scp->fflags & FWRITE) == 0) {
+ DEB(printf("seq_write: unit %d is not for writing.\n", unit));
+ return (EIO);
+ }
+
+ p = 0;
+ countorg = buf->uio_resid;
+ count = countorg;
+
+ s = splmidi();
+ /* Begin playing. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+
+ /* Pick up an event. */
+ while (count >= 4) {
+ if (uiomove((caddr_t)event, 4, buf))
+ printf("seq_write: user memory mangled?\n");
+ ev_code = event[0];
+
+ /* Have a look at the event code. */
+ if (ev_code == SEQ_FULLSIZE) {
+
+ /* A long event, these are the patches/samples for a synthesizer. */
+ midiunit = *(u_short *)&event[2];
+ if (midiunit < 0 || midiunit >= nmidi + nsynth)
+ return (ENXIO);
+ md = &midi_info[midiunit];
+ if (!MIDICONFED(md))
+ return (ENXIO);
+ s = splmidi();
+ if ((md->flags & MIDI_F_BUSY) == 0
+ && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0) {
+ splx(s);
+ return (ENXIO);
+ }
+
+ DEB(printf("seq_write: loading a patch to the unit %d.\n", midiunit));
+
+ ret = md->synth.loadpatch(md, *(short *)&event[0], buf, p + 4, count, 0);
+ splx(s);
+ return (ret);
+ }
+
+ if (ev_code >= 128) {
+
+ /* Some sort of an extended event. The size is eight bytes. */
+#if notyet
+ if (scp->seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) {
+ printf("seq%d: invalid level two event %x.\n", unit, ev_code);
+ return (EINVAL);
+ }
+#endif /* notyet */
+ ev_size = 8;
+
+ if (count < ev_size) {
+ /* No more data. Start playing now. */
+ s = splmidi();
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+
+ return (0);
+ }
+ if (uiomove((caddr_t)&event[4], 4, buf))
+ printf("seq_write: user memory mangled?\n");
+ } else {
+
+ /* Not an extended event. The size is four bytes. */
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ printf("seq%d: four byte event in level two mode.\n", unit);
+ return (EINVAL);
+ }
+#endif /* notyet */
+ ev_size = 4;
+ }
+ if (ev_code == SEQ_MIDIPUTC) {
+ /* An event passed to the midi device itself. */
+ midiunit = event[2];
+ if (midiunit < 0 || midiunit >= nmidi + nsynth)
+ return (ENXIO);
+ md = &midi_info[midiunit];
+ if (!MIDICONFED(md))
+ return (ENXIO);
+ if ((md->flags & MIDI_F_BUSY) == 0
+ && (ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc)) != 0) {
+ seq_reset(scp);
+ return (ret);
+ }
+ }
+
+ /*DEB(printf("seq_write: queueing event %d.\n", event[0]));*/
+ /* Now we queue the event. */
+ switch (seq_queue(scp, event)) {
+ case EAGAIN:
+ s = splmidi();
+ /* The queue is full. Start playing now. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+ return (0);
+ case EINTR:
+ return (EINTR);
+ case ERESTART:
+ return (ERESTART);
+ }
+ p += ev_size;
+ count -= ev_size;
+ }
+
+ /* We have written every single data. Start playing now. */
+ s = splmidi();
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+
+ return (0);
+}
+
+int
+seq_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
+{
+ int unit, midiunit, ret, tmp, s, arg2;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+ struct synth_info *synthinfo;
+ struct midi_info *midiinfo;
+ struct patmgr_info *patinfo;
+ snd_sync_parm *syncparm;
+ struct seq_event_rec *event;
+ struct snd_size *sndsize;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: ioctlling, cmd 0x%x.\n", unit, (int)cmd));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_ioctl: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_ioctl: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ ret = 0;
+
+ switch (cmd) {
+
+ /*
+ * we start with the new ioctl interface.
+ */
+ case AIONWRITE: /* how many bytes can be written ? */
+ *(int *)arg = sd->midi_dbuf_out.fl;
+ break;
+
+ case AIOSSIZE: /* set the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ if (sndsize->play_size <= sd->midi_dbuf_out.unit_size && sndsize->rec_size <= sd->midi_dbuf_in.unit_size) {
+ sd->flags &= ~MIDI_F_HAS_SIZE;
+ sd->midi_dbuf_out.blocksize = sd->midi_dbuf_out.unit_size;
+ sd->midi_dbuf_in.blocksize = sd->midi_dbuf_in.unit_size;
+ }
+ else {
+ if (sndsize->play_size > sd->midi_dbuf_out.bufsize / 4)
+ sndsize->play_size = sd->midi_dbuf_out.bufsize / 4;
+ if (sndsize->rec_size > sd->midi_dbuf_in.bufsize / 4)
+ sndsize->rec_size = sd->midi_dbuf_in.bufsize / 4;
+ /* Round up the size to the multiple of EV_SZ. */
+ sd->midi_dbuf_out.blocksize =
+ ((sndsize->play_size + sd->midi_dbuf_out.unit_size - 1)
+ / sd->midi_dbuf_out.unit_size) * sd->midi_dbuf_out.unit_size;
+ sd->midi_dbuf_in.blocksize =
+ ((sndsize->rec_size + sd->midi_dbuf_in.unit_size - 1)
+ / sd->midi_dbuf_in.unit_size) * sd->midi_dbuf_in.unit_size;
+ sd->flags |= MIDI_F_HAS_SIZE;
+ }
+ /* FALLTHROUGH */
+ case AIOGSIZE: /* get the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ sndsize->play_size = sd->midi_dbuf_out.blocksize;
+ sndsize->rec_size = sd->midi_dbuf_in.blocksize;
+
+ ret = 0;
+ break;
+
+ case AIOSTOP:
+ if (*(int *)arg == AIOSYNC_PLAY) {
+ s = splmidi();
+
+ /* Stop writing. */
+ sd->callback(sd, SEQ_CB_ABORT | SEQ_CB_WR);
+
+ /* Pass the ioctl to the midi devices. */
+ for (midiunit = 0 ; midiunit < nmidi + nsynth ; midiunit++) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md) && scp->midi_open[midiunit] && (md->flags & MIDI_F_WRITING) != 0) {
+ arg2 = *(int *)arg;
+ midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, (caddr_t)&arg2, mode, p);
+ }
+ }
+
+ *(int *)arg = sd->midi_dbuf_out.rl;
+ splx(s);
+ }
+ else if (*(int *)arg == AIOSYNC_CAPTURE) {
+ s = splmidi();
+
+ /* Stop reading. */
+ sd->callback(sd, SEQ_CB_ABORT | SEQ_CB_RD);
+
+ /* Pass the ioctl to the midi devices. */
+ for (midiunit = 0 ; midiunit < nmidi + nsynth ; midiunit++) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md) && scp->midi_open[midiunit] && (md->flags & MIDI_F_WRITING) != 0) {
+ arg2 = *(int *)arg;
+ midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, (caddr_t)&arg2, mode, p);
+ }
+ }
+
+ *(int *)arg = sd->midi_dbuf_in.rl;
+ splx(s);
+ }
+
+ ret = 0;
+ break;
+
+ case AIOSYNC:
+ syncparm = (snd_sync_parm *)arg;
+ scp->sync_parm = *syncparm;
+
+ /* XXX Should select(2) against us watch the blocksize, or sync_parm? */
+
+ ret = 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ case SNDCTL_TMR_TEMPO:
+ case SNDCTL_TMR_START:
+ case SNDCTL_TMR_STOP:
+ case SNDCTL_TMR_CONTINUE:
+ case SNDCTL_TMR_METRONOME:
+ case SNDCTL_TMR_SOURCE:
+#if notyet
+ if (scp->seq_mode != SEQ_2) {
+ ret = EINVAL;
+ break;
+ }
+ ret = tmr->ioctl(tmr_no, cmd, arg);
+#endif /* notyet */
+ break;
+ case SNDCTL_TMR_SELECT:
+#if notyet
+ if (scp->seq_mode != SEQ_2) {
+ ret = EINVAL;
+ break;
+ }
+#endif /* notyet */
+ scp->pending_timer = *(int *)arg;
+ if (scp->pending_timer < 0 || scp->pending_timer >= /*NTIMER*/1) {
+ scp->pending_timer = -1;
+ ret = EINVAL;
+ break;
+ }
+ *(int *)arg = scp->pending_timer;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_PANIC:
+ seq_panic(scp);
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_SYNC:
+ if (mode == O_RDONLY) {
+ ret = 0;
+ break;
+ }
+ ret = seq_sync(scp);
+ break;
+ case SNDCTL_SEQ_RESET:
+ seq_reset(scp);
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_TESTMIDI:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md) && !scp->midi_open[midiunit]) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ break;
+ }
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_GETINCOUNT:
+ if (mode == O_WRONLY)
+ *(int *)arg = 0;
+ else
+ *(int *)arg = sd->midi_dbuf_in.rl;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_GETOUTCOUNT:
+ if (mode == O_RDONLY)
+ *(int *)arg = 0;
+ else
+ *(int *)arg = sd->midi_dbuf_out.fl;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_CTRLRATE:
+#if notyet
+ if (scp->seq_mode != SEQ_2) {
+ ret = tmr->ioctl(tmr_no, cmd, arg);
+ break;
+ }
+#endif /* notyet */
+ if (*(int *)arg != 0) {
+ ret = EINVAL;
+ break;
+ }
+ *(int *)arg = hz;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_RESETSAMPLES:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SEQ_NRSYNTHS:
+ *(int *)arg = nmidi + nsynth;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_NRMIDIS:
+ *(int *)arg = nmidi + nsynth;
+ ret = 0;
+ break;
+ case SNDCTL_SYNTH_MEMAVL:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_FM_4OP_ENABLE:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SYNTH_INFO:
+ synthinfo = (struct synth_info *)arg;
+ midiunit = synthinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SEQ_OUTOFBAND:
+ event = (struct seq_event_rec *)arg;
+ s = splmidi();
+ ret = seq_playevent(scp, event->arr);
+ splx(s);
+ break;
+ case SNDCTL_MIDI_INFO:
+ midiinfo = (struct midi_info *)arg;
+ midiunit = midiinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_PMGR_IFACE:
+ patinfo = (struct patmgr_info *)arg;
+ midiunit = patinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ ret = EBUSY;
+ break;
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_PMGR_ACCESS:
+ patinfo = (struct patmgr_info *)arg;
+ midiunit = patinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SEQ_THRESHOLD:
+ tmp = *(int *)arg;
+ RANGE(tmp, 1, sd->midi_dbuf_out.bufsize - 1);
+ scp->output_threshould = tmp;
+ ret = 0;
+ break;
+ case SNDCTL_MIDI_PRETIME:
+ tmp = *(int *)arg;
+ if (tmp < 0)
+ tmp = 0;
+ tmp = (hz * tmp) / 10;
+ scp->pre_event_timeout = tmp;
+ ret = 0;
+ break;
+ default:
+ if (scp->fflags == O_RDONLY) {
+ ret = EIO;
+ break;
+ }
+ if (!scp->midi_open[0]) {
+ md = &midi_info[0];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), 0, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ }
+
+ return (ret);
+}
+
+int
+seq_poll(dev_t i_dev, int events, struct proc *p)
+{
+ int unit, ret, s, lim;
+ sc_p scp;
+ seqdev_info *sd;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: polling.\n", unit));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_poll: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_poll: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ ret = 0;
+ s = splmidi();
+
+ /* Look up the apropriate queue and select it. */
+ if ((events & (POLLOUT | POLLWRNORM)) != 0) {
+ /* Start playing. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+
+ /* Find out the boundary. */
+ if ((sd->flags & SEQ_F_HAS_SIZE) != 0)
+ lim = sd->midi_dbuf_out.blocksize;
+ else
+ lim = sd->midi_dbuf_out.unit_size;
+ if (sd->midi_dbuf_out.fl < lim)
+ /* No enough space, record select. */
+ selrecord(p, &sd->midi_dbuf_out.sel);
+ else
+ /* We can write now. */
+ ret |= events & (POLLOUT | POLLWRNORM);
+ }
+ if ((events & (POLLIN | POLLRDNORM)) != 0) {
+ /* Start recording. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_RD);
+
+ /* Find out the boundary. */
+ if ((sd->flags & SEQ_F_HAS_SIZE) != 0)
+ lim = sd->midi_dbuf_in.blocksize;
+ else
+ lim = sd->midi_dbuf_in.unit_size;
+ if (sd->midi_dbuf_in.rl < lim)
+ /* No data ready, record select. */
+ selrecord(p, &sd->midi_dbuf_in.sel);
+ else
+ /* We can write now. */
+ ret |= events & (POLLIN | POLLRDNORM);
+ }
+ splx(s);
+
+ return (ret);
+}
+
+static void
+seq_intr(void *p, mididev_info *md)
+{
+ sc_p scp;
+ seqdev_info *sd;
+
+ sd = (seqdev_info *)p;
+ scp = sd->softc;
+
+ /* Restart playing if we have the data to output. */
+ if (scp->queueout_pending)
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ /* Check the midi device if we are reading. */
+ if ((sd->flags & SEQ_F_READING) != 0)
+ seq_midiinput(scp, md);
+}
+
+static int
+seq_callback(seqdev_info *sd, int reason)
+{
+ int unit;
+ sc_p scp;
+
+ /*DEB(printf("seq_callback: reason 0x%x.\n", reason));*/
+
+ if (sd == NULL) {
+ DEB(printf("seq_callback: device not configured.\n"));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+ unit = sd->unit;
+
+ switch (reason & SEQ_CB_REASON_MASK) {
+ case SEQ_CB_START:
+ if ((reason & SEQ_CB_RD) != 0 && (sd->flags & SEQ_F_READING) == 0)
+ /* Begin recording. */
+ sd->flags |= SEQ_F_READING;
+ if ((reason & SEQ_CB_WR) != 0 && (sd->flags & SEQ_F_WRITING) == 0)
+ /* Start playing. */
+ seq_startplay(scp);
+ break;
+ case SEQ_CB_STOP:
+ case SEQ_CB_ABORT:
+ if ((reason & SEQ_CB_RD) != 0 && (sd->flags & SEQ_F_READING) != 0) {
+ /* Stop the timer. */
+ scp->seq_time = seq_gettime();
+ scp->prev_input_time = 0;
+
+ /* Stop recording. */
+ sd->flags &= ~SEQ_F_READING;
+ }
+ if ((reason & SEQ_CB_WR) != 0 && (sd->flags & SEQ_F_WRITING) != 0) {
+ /* Stop the timer. */
+ seq_stoptimer(scp);
+ scp->seq_time = seq_gettime();
+ scp->prev_event_time = 0;
+
+ /* Stop Playing. */
+ sd->flags &= ~SEQ_F_WRITING;
+ scp->queueout_pending = 0;
+ }
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+static int
+seq_queue(sc_p scp, u_char *note)
+{
+ int unit, err, s;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ /*DEB(printf("seq%d: queueing.\n", unit));*/
+
+ s = splmidi();
+
+ /* Start playing if we have some data in the queue. */
+ if (sd->midi_dbuf_out.rl >= EV_SZ)
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+
+ if (sd->midi_dbuf_out.fl < EV_SZ) {
+ /* We have no space. Start playing if not yet. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ if ((sd->flags & SEQ_F_NBIO) != 0 && sd->midi_dbuf_out.fl < EV_SZ) {
+ /* We would block. */
+ splx(s);
+ return (EAGAIN);
+ } else
+ while (sd->midi_dbuf_out.fl < EV_SZ) {
+ /* We have no space. Good night. */
+ err = tsleep(&sd->midi_dbuf_out.tsleep_out, PRIBIO | PCATCH, "seqque", 0);
+ if (err == EINTR)
+ sd->callback(sd, SEQ_CB_STOP | SEQ_CB_WR);
+ if (err == EINTR || err == ERESTART) {
+ splx(s);
+ return (err);
+ }
+ }
+ }
+
+ /* We now have enough space to write. */
+ err = midibuf_seqwrite(&sd->midi_dbuf_out, note, EV_SZ);
+
+ splx(s);
+
+ if (err < 0)
+ err = -err;
+ else
+ err = 0;
+
+ return (err);
+}
+
+static void
+seq_startplay(sc_p scp)
+{
+ int unit;
+ u_char event[EV_SZ];
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ /* Dequeue the events to play. */
+ while (sd->midi_dbuf_out.rl >= EV_SZ) {
+
+ /* We are playing now. */
+ sd->flags |= SEQ_F_WRITING;
+
+ /* We only copy the event, not dequeue. */
+ midibuf_seqcopy(&sd->midi_dbuf_out, event, EV_SZ);
+
+ switch (seq_playevent(scp, event)) {
+ case TIMERARMED:
+ /* Dequeue the event. */
+ midibuf_seqread(&sd->midi_dbuf_out, event, EV_SZ);
+ /* FALLTHRU */
+ case QUEUEFULL:
+ /* We cannot play further. */
+ return;
+ case MORE:
+ /* Dequeue the event. */
+ midibuf_seqread(&sd->midi_dbuf_out, event, EV_SZ);
+ break;
+ }
+ }
+
+ /* Played every event in the queue. */
+ sd->flags &= ~SEQ_F_WRITING;
+}
+
+static int
+seq_playevent(sc_p scp, u_char *event)
+{
+ int unit, ret;
+ long *delay;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ md = &midi_info[0];
+ if (!MIDICONFED(md))
+ return (MORE);
+
+ switch(event[0]) {
+ case SEQ_NOTEOFF:
+ if ((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)
+ if (md->synth.killnote(md, event[1], 255, event[3]) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_NOTEON:
+ if (((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)
+ && (event[4] < 128 || event[4] == 255))
+ if (md->synth.startnote(md, event[1], event[2], event[3]) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_WAIT:
+
+ /* Extract the delay. */
+ delay = (long *)event;
+ *delay = (*delay >> 8) & 0xffffff;
+ if (*delay > 0) {
+ /* Arm the timer. */
+ sd->flags |= SEQ_F_WRITING;
+ scp->prev_event_time = *delay;
+ if (seq_requesttimer(scp, *delay)) {
+ ret = TIMERARMED;
+ break;
+ }
+ }
+ ret = MORE;
+ break;
+ case SEQ_PGMCHANGE:
+ if ((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)
+ if (md->synth.setinstr(md, event[1], event[2]) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_SYNCTIMER:
+ /* Reset the timer. */
+ scp->seq_time = seq_gettime();
+ scp->prev_input_time = 0;
+ scp->prev_event_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+ ret = MORE;
+ break;
+ case SEQ_MIDIPUTC:
+ /* Pass through to the midi device. */
+ if (event[2] < nmidi + nsynth) {
+ md = &midi_info[event[2]];
+ if (MIDICONFED(md) && ((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)) {
+ if (md->synth.writeraw(md, &event[1], sizeof(event[1]), 1) == EAGAIN) {
+ /* The queue was full. Try again later. */
+ ret = QUEUEFULL;
+ break;
+ }
+ }
+ }
+ ret = MORE;
+ break;
+ case SEQ_ECHO:
+ /* Echo this event back. */
+ if (seq_copytoinput(scp, event, 4) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_PRIVATE:
+ if (event[1] < nmidi + nsynth) {
+ md = &midi_info[event[1]];
+ if (MIDICONFED(md) && md->synth.hwcontrol(md, event) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ }
+ ret = MORE;
+ break;
+ case SEQ_EXTENDED:
+ ret = seq_extended(scp, event);
+ break;
+ case EV_CHN_VOICE:
+ ret = seq_chnvoice(scp, event);
+ break;
+ case EV_CHN_COMMON:
+ ret = seq_chncommon(scp, event);
+ break;
+ case EV_TIMING:
+ ret = seq_timing(scp, event);
+ break;
+ case EV_SEQ_LOCAL:
+ ret = seq_local(scp, event);
+ break;
+ case EV_SYSEX:
+ ret = seq_sysex(scp, event);
+ break;
+ default:
+ ret = MORE;
+ break;
+ }
+
+ switch (ret) {
+ case QUEUEFULL:
+ /*DEB(printf("seq_playevent: the queue is full.\n"));*/
+ /* The queue was full. Try again on the interrupt by the midi device. */
+ sd->flags |= SEQ_F_WRITING;
+ scp->queueout_pending = 1;
+ break;
+ case TIMERARMED:
+ sd->flags |= SEQ_F_WRITING;
+ /* FALLTHRU */
+ case MORE:
+ scp->queueout_pending = 0;
+ break;
+ }
+
+ return (ret);
+}
+
+static u_long
+seq_gettime(void)
+{
+ struct timeval timecopy;
+
+ getmicrotime(&timecopy);
+ return timecopy.tv_usec / (1000000 / hz) + (u_long) timecopy.tv_sec * hz;
+}
+
+static int
+seq_requesttimer(sc_p scp, int delay)
+{
+ u_long cur_time, rel_base;
+
+ /*DEB(printf("seq%d: requested timer at delay of %d.\n", unit, delay));*/
+
+ cur_time = seq_gettime();
+
+ if (delay < 0)
+ /* Request a new timer. */
+ delay = -delay;
+ else {
+ rel_base = cur_time - scp->seq_time;
+ if (delay <= rel_base)
+ return 0;
+ delay -= rel_base;
+ }
+
+#if notdef
+ /*
+ * Compensate the delay of midi message transmission.
+ * XXX Do we have to consider the accumulation of errors
+ * less than 1/hz second?
+ */
+ delay -= (cur_time - scp->prev_wakeup_time);
+ if (delay < 1) {
+ printf("sequencer: prev = %lu, cur = %lu, delay = %d, skip sleeping.\n",
+ scp->prev_wakeup_time, cur_time, delay);
+ seq_stoptimer(scp);
+ return 0;
+ }
+#endif /* notdef */
+
+ scp->timeout_ch = timeout(seq_timer, (void *)scp, delay);
+ scp->timer_running = 1;
+ return 1;
+}
+
+static void
+seq_stoptimer(sc_p scp)
+{
+ /*DEB(printf("seq%d: stopping timer.\n", unit));*/
+
+ if (scp->timer_running) {
+ untimeout(seq_timer, (void *)scp, scp->timeout_ch);
+ scp->timer_running = 0;
+ }
+}
+
+static void
+seq_midiinput(sc_p scp, mididev_info *md)
+{
+ int unit, midiunit;
+ u_long tstamp;
+ u_char event[4];
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ /* Can this midi device interrupt for input? */
+ midiunit = md->unit;
+ if (scp->midi_open[midiunit]
+ && (md->flags & MIDI_F_READING) != 0
+ && md->intrarg == sd)
+ /* Read the input data. */
+ while (md->synth.readraw(md, &event[1], sizeof(event[1]), 1) == 0) {
+ tstamp = seq_gettime() - scp->seq_time;
+ if (tstamp != scp->prev_input_time) {
+ /* Insert a wait between events. */
+ tstamp = (tstamp << 8) | SEQ_WAIT;
+ seq_copytoinput(scp, (u_char *)&tstamp, 4);
+ scp->prev_input_time = tstamp;
+ }
+ event[0] = SEQ_MIDIPUTC;
+ event[2] = midiunit;
+ event[3] = 0;
+ seq_copytoinput(scp, event, sizeof(event));
+ }
+}
+
+static int
+seq_copytoinput(sc_p scp, u_char *event, int len)
+{
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+
+ if (midibuf_input_intr(&sd->midi_dbuf_in, event, len) == -EAGAIN)
+ return (EAGAIN);
+
+ return (0);
+}
+
+static int
+seq_extended(sc_p scp, u_char *event)
+{
+ int unit;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ if (event[2] >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[event[2]];
+ if (!MIDICONFED(md) && (md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0)
+ return (MORE);
+
+ switch (event[1]) {
+ case SEQ_NOTEOFF:
+ if (md->synth.killnote(md, event[3], event[4], event[5]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_NOTEON:
+ if (event[4] < 128 || event[4] == 255)
+ if (md->synth.startnote(md, event[3], event[4], event[5]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_PGMCHANGE:
+ if (md->synth.setinstr(md, event[3], event[4]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_AFTERTOUCH:
+ if (md->synth.aftertouch(md, event[3], event[4]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_BALANCE:
+ if (md->synth.panning(md, event[3], (char)event[4]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_CONTROLLER:
+ if (md->synth.controller(md, event[3], event[4], *(short *)&event[5]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_VOLMODE:
+ if (md->synth.volumemethod != NULL)
+ if (md->synth.volumemethod(md, event[3]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_chnvoice(sc_p scp, u_char *event)
+{
+ int voice;
+ seqdev_info *sd;
+ mididev_info *md;
+ u_char dev, cmd, chn, note, parm;
+
+ voice = -1;
+ dev = event[1];
+ cmd = event[2];
+ chn = event[3];
+ note = event[4];
+ parm = event[5];
+
+ sd = scp->devinfo;
+
+ if (dev >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[dev];
+ if (!MIDICONFED(md) && (md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0)
+ return (MORE);
+
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ if (md->synth.allocvoice)
+ voice = seq_allocvoice(md, chn, note);
+ }
+#endif /* notyet */
+ switch (cmd) {
+ case MIDI_NOTEON:
+ if (note < 128 || note == 255) {
+#if notyet
+ if (voice == -1 && scp->seq_mode == SEQ_2 && md->synth.allocvoice)
+ /* This is an internal synthesizer. (FM, GUS, etc) */
+ if ((voice = seq_allocvoice(md, chn, note)) == -EAGAIN)
+ return (QUEUEFULL);
+#endif /* notyet */
+ if (voice == -1)
+ voice = chn;
+
+#if notyet
+ if (scp->seq_mode == SEQ_2 && dev < nmidi + nsynth && chn == 9) {
+ /* This channel is a percussion. The note number is the patch number. */
+ if (md->synth.setinstr(md, voice, 128 + note) == EAGAIN)
+ return (QUEUEFULL);
+ note = 60; /* Middle C. */
+ }
+ if (scp->seq_mode == SEQ_2)
+ if (md->synth.setupvoice(md, voice, chn) == EAGAIN)
+ return (QUEUEFULL);
+#endif /* notyet */
+ if (md->synth.startnote(md, voice, note, parm) == EAGAIN)
+ return (QUEUEFULL);
+ }
+ break;
+ case MIDI_NOTEOFF:
+ if (voice == -1)
+ voice = chn;
+ if (md->synth.killnote(md, voice, note, parm) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case MIDI_KEY_PRESSURE:
+ if (voice == -1)
+ voice = chn;
+ if (md->synth.aftertouch(md, voice, parm) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_findvoice(mididev_info *md, int chn, int note)
+{
+ int i;
+ u_short key;
+
+ key = (chn << 8) | (note + 1);
+
+ for (i = 0 ; i < md->synth.alloc.max_voice ; i++)
+ if (md->synth.alloc.map[i] == key)
+ return (i);
+
+ return (-1);
+}
+
+static int
+seq_allocvoice(mididev_info *md, int chn, int note)
+{
+ int voice;
+ u_short key;
+
+ key = (chn << 8) | (note + 1);
+
+ if ((voice = md->synth.allocvoice(md, chn, note, &md->synth.alloc)) == -EAGAIN)
+ return (-EAGAIN);
+ md->synth.alloc.map[voice] = key;
+ md->synth.alloc.alloc_times[voice] = md->synth.alloc.timestamp++;
+
+ return (voice);
+}
+
+static int
+seq_chncommon(sc_p scp, u_char *event)
+{
+ int unit/*, i, val, key*/;
+ u_short w14;
+ u_char dev, cmd, chn, p1;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ dev = event[1];
+ cmd = event[2];
+ chn = event[3];
+ p1 = event[4];
+ w14 = *(u_short *)&event[6];
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ if (dev >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[dev];
+ if (!MIDICONFED(md) && (md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0)
+ return (MORE);
+
+ switch (cmd) {
+ case MIDI_PGM_CHANGE:
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ md->synth.chn_info[chn].pgm_num = p1;
+ if (dev < nmidi + nsynth)
+ if (md->synth.setinstr(md, chn, p1) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+#endif /* notyet */
+ /* For Mode 1. */
+ if (md->synth.setinstr(md, chn, p1) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case MIDI_CTL_CHANGE:
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ if (chn < 16 && p1 < 128) {
+ md->synth.chn_info[chn].controllers[p1] = w14 & 0x7f;
+ if (p1 < 32)
+ /* We have set the MSB, clear the LSB. */
+ md->synth.chn_info[chn].controllers[p1 + 32] = 0;
+ if (dev < nmidi + nsynth) {
+ val = w14 & 0x7f;
+ if (p1 < 64) {
+ /* Combine the MSB and the LSB. */
+ val = ((md->synth.chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
+ | (md->synth.chn_info[chn].controllers[p1 | 32] & 0x7f);
+ p1 &= ~32;
+ }
+ /* Handle all of the notes playing on this channel. */
+ key = ((int)chn << 8);
+ for (i = 0 ; i < md->synth.alloc.max_voice ; i++)
+ if ((md->synth.alloc.map[i] & 0xff00) == key)
+ if (md->synth.controller(md, i, p1, val) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+ if (md->synth.controller(md, chn, p1, w14) == EAGAIN)
+ return (QUEUEFULL);
+ }
+ } else
+#endif /* notyet */
+ /* For Mode 1. */
+ if (md->synth.controller(md, chn, p1, w14) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case MIDI_PITCH_BEND:
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ md->synth.chn_info[chn].bender_value = w14;
+ if (dev < nmidi + nsynth) {
+ /* Handle all of the notes playing on this channel. */
+ key = ((int)chn << 8);
+ for (i = 0 ; i < md->synth.alloc.max_voice ; i++)
+ if ((md->synth.alloc.map[i] & 0xff00) == key)
+ if (md->synth.bender(md, i, w14) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+ if (md->synth.bender(md, chn, w14) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+#endif /* notyet */
+ /* For Mode 1. */
+ if (md->synth.bender(md, chn, w14) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_timing(sc_p scp, u_char *event)
+{
+ int unit/*, ret*/;
+ long parm;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ parm = *(long *)&event[4];
+
+#if notyet
+ if (scp->seq_mode == SEQ_2 && (ret = tmr->event(tmr_no, event)) == TIMERARMED)
+ return (ret);
+#endif /* notyet */
+ switch (event[1]) {
+ case TMR_WAIT_REL:
+ parm += scp->prev_event_time;
+ /* FALLTHRU */
+ case TMR_WAIT_ABS:
+ if (parm > 0) {
+ sd->flags |= SEQ_F_WRITING;
+ scp->prev_event_time = parm;
+ if (seq_requesttimer(scp, parm))
+ return (TIMERARMED);
+ }
+ break;
+ case TMR_START:
+ scp->seq_time = seq_gettime();
+ scp->prev_input_time = 0;
+ scp->prev_event_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+ break;
+ case TMR_STOP:
+ break;
+ case TMR_CONTINUE:
+ break;
+ case TMR_TEMPO:
+ break;
+ case TMR_ECHO:
+#if notyet
+ if (scp->seq_mode == SEQ_2)
+ seq_copytoinput(scp, event, 8);
+ else {
+#endif /* notyet */
+ parm = (parm << 8 | SEQ_ECHO);
+ seq_copytoinput(scp, (u_char *)&parm, 4);
+#if notyet
+ }
+#endif /* notyet */
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_local(sc_p scp, u_char *event)
+{
+ int unit;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ switch (event[1]) {
+ case LOCL_STARTAUDIO:
+#if notyet
+ DMAbuf_start_devices(*(u_int *)&event[4]);
+#endif /* notyet */
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_sysex(sc_p scp, u_char *event)
+{
+ int unit, i, l;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ if (event[1] >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[event[1]];
+ if (!MIDICONFED(md) || md->synth.sendsysex == NULL
+ || ((md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0))
+ return (MORE);
+
+ l = 0;
+ for (i = 0 ; i < 6 && event[i + 2] != 0xff ; i++)
+ l = i + 1;
+ if (l > 0)
+ if (md->synth.sendsysex(md, &event[2], l) == EAGAIN)
+ return (QUEUEFULL);
+
+ return (MORE);
+}
+
+static void
+seq_timer(void *arg)
+{
+ sc_p scp;
+
+ scp = arg;
+
+ /*DEB(printf("seq_timer: timer fired.\n"));*/
+
+ /* Record the current timestamp. */
+ scp->prev_wakeup_time = seq_gettime();
+
+ seq_startplay(scp);
+}
+
+static int
+seq_openmidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p)
+{
+ int midiunit, s, err;
+
+ if (md == NULL || !MIDICONFED(md)) {
+ DEB(printf("seq_openmidi: midi device does not exist.\n"));
+ return (ENXIO);
+ }
+ midiunit = md->unit;
+
+ DEB(printf("seq_openmidi: opening midi unit %d.\n", midiunit));
+
+ if (!scp->midi_open[midiunit]) {
+ err = midi_open(MIDIMKDEV(MIDI_CDEV_MAJOR, midiunit, SND_DEV_MIDIN), flags, mode, p);
+ if (err != 0) {
+ printf("seq_openmidi: failed to open midi device %d.\n", midiunit);
+ return (err);
+ }
+ s = splmidi();
+ scp->midi_open[midiunit] = 1;
+ md->intr = seq_intr;
+ md->intrarg = scp->devinfo;
+ md->synth.prev_out_status = 0;
+ md->synth.sysex_state = 0;
+ splx(s);
+ }
+
+ return (0);
+}
+
+static int
+seq_closemidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p)
+{
+ int midiunit, s;
+
+ if (md == NULL || !MIDICONFED(md)) {
+ DEB(printf("seq_closemidi: midi device does not exist.\n"));
+ return (ENXIO);
+ }
+ midiunit = md->unit;
+
+ DEB(printf("seq_closemidi: closing midi unit %d.\n", midiunit));
+
+ if (scp->midi_open[midiunit]) {
+ midi_close(MIDIMKDEV(MIDI_CDEV_MAJOR, midiunit, SND_DEV_MIDIN), flags, mode, p);
+ s = splmidi();
+ scp->midi_open[midiunit] = 0;
+ md->intr = NULL;
+ md->intrarg = NULL;
+ splx(s);
+ }
+
+ return (0);
+}
+
+static void
+seq_panic(sc_p scp)
+{
+ seq_reset(scp);
+}
+
+static int
+seq_reset(sc_p scp)
+{
+ int unit, i, s, chn;
+ seqdev_info *sd;
+ mididev_info *md;
+ u_char c[3];
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ s = splmidi();
+
+ /* Stop reading and writing. */
+ sd->callback(sd, SEQ_CB_ABORT | SEQ_CB_RD | SEQ_CB_WR);
+
+ /* Clear the queues. */
+ midibuf_init(&sd->midi_dbuf_in);
+ midibuf_init(&sd->midi_dbuf_out);
+
+#if notyet
+ /* Reset the synthesizers. */
+ for (i = 0 ; i < nmidi + nsynth ; i++) {
+ md = &midi_info[i];
+ if (MIDICONFED(md) && scp->midi_open[i])
+ md->synth.reset(md);
+ }
+#endif /* notyet */
+
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ for (chn = 0 ; chn < 16 ; chn++)
+ for (i = 0 ; i < nmidi + nsynth ; i++)
+ if (midi_open[i]) {
+ md = &midi_info[i];
+ if (!MIDICONFED(md))
+ continue;
+ if (md->synth.controller(md, chn, 123, 0) == EAGAIN /* All notes off. */
+ || md->synth.controller(md, chn, 121, 0) == EAGAIN /* Reset all controllers. */
+ || md->synth.bender(md, chn, 1 << 13) == EAGAIN) { /* Reset pitch bend. */
+ splx(s);
+ return (EAGAIN);
+ }
+ }
+ splx(s);
+ } else {
+#endif /* notyet */
+ splx(s);
+ for (i = 0 ; i < nmidi + nsynth ; i++) {
+ md = &midi_info[i];
+ if (!MIDICONFED(md))
+ continue;
+
+ /* Send active sensing. */
+ c[0] = 0xfe; /* Active Sensing. */
+ if (md->synth.writeraw(md, c, 1, 0) == EAGAIN)
+ return (EAGAIN);
+ /*
+ * We need a sleep to reset a midi device using an active sensing.
+ * SC-88 resets after 420ms...
+ */
+ tsleep(md, PRIBIO, "seqrst", 500 * hz / 1000);
+ for (chn = 0 ; chn < 16 ; chn++) {
+ c[0] = 0xb0 | (chn & 0x0f);
+ c[1] = (u_char)0x78; /* All sound off */
+ c[2] = (u_char)0;
+ if (md->synth.writeraw(md, c, 3, 0) == EAGAIN)
+ return (EAGAIN);
+ c[1] = (u_char)0x7b; /* All note off */
+ if (md->synth.writeraw(md, c, 3, 0) == EAGAIN)
+ return (EAGAIN);
+ c[1] = (u_char)0x79; /* Reset all controller */
+ if (md->synth.writeraw(md, c, 3, 0) == EAGAIN)
+ return (EAGAIN);
+ }
+ }
+ for (i = 0 ; i < nmidi + nsynth ; i++){
+ md = &midi_info[i];
+ if (MIDICONFED(md))
+ seq_closemidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ }
+#if notyet
+ }
+#endif /* notyet */
+
+ return (0);
+}
+
+static int
+seq_sync(sc_p scp)
+{
+ int unit, s, i;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ s = splmidi();
+
+ if (sd->midi_dbuf_out.rl >= EV_SZ)
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+
+ while ((sd->flags & SEQ_F_WRITING) != 0 && sd->midi_dbuf_out.rl >= EV_SZ) {
+ i = tsleep(&sd->midi_dbuf_out.tsleep_out, PRIBIO | PCATCH, "seqsnc", 0);
+ if (i == EINTR)
+ sd->callback(sd, SEQ_CB_STOP | SEQ_CB_WR);
+ if (i == EINTR || i == ERESTART) {
+ splx(s);
+ return (i);
+ }
+ }
+ splx(s);
+
+ return (0);
+}
+
+/*
+ * a small utility function which, given a device number, returns
+ * a pointer to the associated seqdev_info struct, and sets the unit
+ * number.
+ */
+static seqdev_info *
+get_seqdev_info(dev_t i_dev, int *unit)
+{
+ int u;
+ seqdev_info *d = NULL ;
+
+ if (MIDIDEV(i_dev) != SND_DEV_SEQ && MIDIDEV(i_dev) != SND_DEV_SEQ2)
+ return NULL;
+ u = MIDIUNIT(i_dev);
+ if (unit)
+ *unit = u ;
+
+ if (u >= NSEQ_MAX) {
+ DEB(printf("get_seqdev_info: unit %d is not configured.\n", u));
+ return NULL;
+ }
+ d = &seq_info[u];
+
+ return d ;
+}
+
+/* XXX These functions are actually redundant. */
+static int
+seqopen(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_open(i_dev, flags, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqclose(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_close(i_dev, flags, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqread(dev_t i_dev, struct uio * buf, int flag)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_read(i_dev, buf, flag);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqwrite(dev_t i_dev, struct uio * buf, int flag)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_write(i_dev, buf, flag);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_ioctl(i_dev, cmd, arg, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqpoll(dev_t i_dev, int events, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_poll(i_dev, events, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seq_modevent(module_t mod, int type, void *data)
+{
+ int retval;
+
+ retval = 0;
+
+ switch (type) {
+ case MOD_LOAD:
+ seq_init();
+ break;
+
+ case MOD_UNLOAD:
+ printf("sequencer: unload not supported yet.\n");
+ retval = EOPNOTSUPP;
+ break;
+
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+DEV_MODULE(seq, seq_modevent, NULL);
OpenPOWER on IntegriCloud