summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/midi/midisynth.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound/midi/midisynth.c')
-rw-r--r--sys/dev/sound/midi/midisynth.c532
1 files changed, 532 insertions, 0 deletions
diff --git a/sys/dev/sound/midi/midisynth.c b/sys/dev/sound/midi/midisynth.c
new file mode 100644
index 0000000..d370833
--- /dev/null
+++ b/sys/dev/sound/midi/midisynth.c
@@ -0,0 +1,532 @@
+/*
+ * 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 interface for a sequencer to interact a midi driver.
+ * This interface translates the sequencer operations to the corresponding
+ * midi messages, and vice versa.
+ */
+
+#include <dev/sound/midi/midi.h>
+
+#define TYPEDRANGE(type, x, lower, upper) \
+{ \
+ type tl, tu; \
+ tl = (lower); \
+ tu = (upper); \
+ if (x < tl) { \
+ x = tl; \
+ } else if(x > tu) { \
+ x = tu; \
+ } \
+}
+
+/*
+ * These functions goes into midisynthdev_op_desc.
+ */
+static mdsy_killnote_t synth_killnote;
+static mdsy_setinstr_t synth_setinstr;
+static mdsy_startnote_t synth_startnote;
+static mdsy_reset_t synth_reset;
+static mdsy_hwcontrol_t synth_hwcontrol;
+static mdsy_loadpatch_t synth_loadpatch;
+static mdsy_panning_t synth_panning;
+static mdsy_aftertouch_t synth_aftertouch;
+static mdsy_controller_t synth_controller;
+static mdsy_patchmgr_t synth_patchmgr;
+static mdsy_bender_t synth_bender;
+static mdsy_allocvoice_t synth_allocvoice;
+static mdsy_setupvoice_t synth_setupvoice;
+static mdsy_sendsysex_t synth_sendsysex;
+static mdsy_prefixcmd_t synth_prefixcmd;
+static mdsy_volumemethod_t synth_volumemethod;
+static mdsy_readraw_t synth_readraw;
+static mdsy_writeraw_t synth_writeraw;
+
+/*
+ * This is the synthdev_info for a midi interface device.
+ * You may have to replace a few of functions for an internal
+ * synthesizer.
+ */
+synthdev_info midisynth_op_desc = {
+ synth_killnote,
+ synth_setinstr,
+ synth_startnote,
+ synth_reset,
+ synth_hwcontrol,
+ synth_loadpatch,
+ synth_panning,
+ synth_aftertouch,
+ synth_controller,
+ synth_patchmgr,
+ synth_bender,
+ synth_allocvoice,
+ synth_setupvoice,
+ synth_sendsysex,
+ synth_prefixcmd,
+ synth_volumemethod,
+ synth_readraw,
+ synth_writeraw,
+};
+
+/* The following functions are local. */
+static int synth_leavesysex(mididev_info *md);
+
+/*
+ * Here are the main functions to interact to the midi sequencer.
+ * These are called from the sequencer functions in sequencer.c.
+ */
+
+static int
+synth_killnote(mididev_info *md, int chn, int note, int vel)
+{
+ int unit, lenw;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (note < 0 || note > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ TYPEDRANGE(int, vel, 0, 127);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ if (vel == 64) {
+ c[0] = 0x90 | (chn & 0x0f); /* Note on. */
+ c[1] = (u_char)note;
+ c[2] = 0;
+ } else {
+ c[0] = 0x80 | (chn & 0x0f); /* Note off. */
+ c[1] = (u_char)note;
+ c[2] = (u_char)vel;
+ }
+
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+
+ return (md->synth.writeraw(md, c, 3, &lenw, 1));
+}
+
+static int
+synth_setinstr(mididev_info *md, int chn, int instr)
+{
+ int unit, lenw;
+ synthdev_info *sd;
+ u_char c[2];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (instr < 0 || instr > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ c[0] = 0xc0 | (chn & 0x0f); /* Progamme change. */
+ c[1] = (u_char)instr;
+
+ return (md->synth.writeraw(md, c, 3, &lenw, 1));
+}
+
+static int
+synth_startnote(mididev_info *md, int chn, int note, int vel)
+{
+ int unit, lenw;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (note < 0 || note > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ TYPEDRANGE(int, vel, 0, 127);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ c[0] = 0x90 | (chn & 0x0f); /* Note on. */
+ c[1] = (u_char)note;
+ c[2] = (u_char)vel;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+
+ return (md->synth.writeraw(md, c, 3, &lenw, 1));
+}
+
+static int
+synth_reset(mididev_info *md)
+{
+ synth_leavesysex(md);
+ return (0);
+}
+
+static int
+synth_hwcontrol(mididev_info *md, u_char *event)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_loadpatch(mididev_info *md, int format, struct uio *buf, int offs, int count, int pmgr_flag)
+{
+ struct sysex_info sysex;
+ synthdev_info *sd;
+ int unit, i, eox_seen, first_byte, left, src_offs, hdr_size, lenw;
+ u_char c[count];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ eox_seen = 0;
+ first_byte = 1;
+ hdr_size = offsetof(struct sysex_info, data);
+
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ if (synth_prefixcmd(md, 0xf0))
+ return (0);
+ if (format != SYSEX_PATCH) {
+ printf("synth_loadpatch: patch format 0x%x is invalid.\n", format);
+ return (EINVAL);
+ }
+ if (count < hdr_size) {
+ printf("synth_loadpatch: patch header is too short.\n");
+ return (EINVAL);
+ }
+ count -= hdr_size;
+
+ /* Copy the patch data. */
+ if (uiomove((caddr_t)&((char *)&sysex)[offs], hdr_size - offs, buf))
+ printf("synth_loadpatch: memory mangled?\n");
+
+ if (count < sysex.len) {
+ sysex.len = (long)count;
+ printf("synth_loadpatch: sysex record of %d bytes is too long, adjusted to %d bytes.\n", (int)sysex.len, count);
+ }
+ left = sysex.len;
+ src_offs = 0;
+
+ for (i = 0 ; i < left ; i++) {
+ uiomove((caddr_t)&c[i], 1, buf);
+ eox_seen = i > 0 && (c[i] & 0x80) != 0;
+ if (eox_seen && c[i] != 0xf7)
+ c[i] = 0xf7;
+ if (i == 0 && c[i] != 0x80) {
+ printf("synth_loadpatch: sysex does not begin with the status.\n");
+ return (EINVAL);
+ }
+ if (!first_byte && (c[i] & 0x80) != 0) {
+ md->synth.writeraw(md, c, i + 1, &lenw, 0);
+ return (0);
+ }
+ first_byte = 0;
+ }
+
+ if (!eox_seen) {
+ c[0] = 0xf7;
+ md->synth.writeraw(md, c, 1, &lenw, 0);
+ }
+
+ return (0);
+}
+
+static int
+synth_panning(mididev_info *md, int chn, int pan)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_aftertouch(mididev_info *md, int chn, int press)
+{
+ int unit, lenw;
+ synthdev_info *sd;
+ u_char c[2];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (press < 0 || press > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ c[0] = 0xd0 | (chn & 0x0f); /* Channel Pressure. */
+ c[1] = (u_char)press;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+
+ return (md->synth.writeraw(md, c, 2, &lenw, 1));
+}
+
+static int
+synth_controller(mididev_info *md, int chn, int ctrlnum, int val)
+{
+ int unit, lenw;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (ctrlnum < 1 || ctrlnum > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ c[0] = 0xb0 | (chn & 0x0f); /* Control Message. */
+ c[1] = (u_char)ctrlnum;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+
+ return (md->synth.writeraw(md, c, 3, &lenw, 1));
+}
+
+static int
+synth_patchmgr(mididev_info *md, struct patmgr_info *rec)
+{
+ return (EINVAL);
+}
+
+static int
+synth_bender(mididev_info *md, int chn, int val)
+{
+ int unit, lenw;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (val < 0 || val > 16383 || chn < 0 || chn > 15)
+ return (EINVAL);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ c[0] = 0xe0 | (chn & 0x0f); /* Pitch bend. */
+ c[1] = (u_char)val & 0x7f;
+ c[2] = (u_char)(val >> 7) & 0x7f;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+
+ return (md->synth.writeraw(md, c, 3, &lenw, 1));
+}
+
+static int
+synth_allocvoice(mididev_info *md, int chn, int note, struct voice_alloc_info *alloc)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_setupvoice(mididev_info *md, int voice, int chn)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_sendsysex(mididev_info *md, u_char *sysex, int len)
+{
+ int unit, i, lenw;
+ synthdev_info *sd;
+ u_char c[len];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ mtx_lock(&sd->status_mtx);
+ for (i = 0 ; i < len ; i++) {
+ switch (sysex[i]) {
+ case 0xf0:
+ /* Sysex begins. */
+ if (synth_prefixcmd(md, 0xf0)) {
+ mtx_unlock(&sd->status_mtx);
+ return (0);
+ }
+ sd->sysex_state = 1;
+ break;
+ case 0xf7:
+ /* Sysex ends. */
+ if (!sd->sysex_state) {
+ mtx_unlock(&sd->status_mtx);
+ return (0);
+ }
+ sd->sysex_state = 0;
+ break;
+ default:
+ if (!sd->sysex_state) {
+ mtx_unlock(&sd->status_mtx);
+ return (0);
+ }
+ if ((sysex[i] & 0x80) != 0) {
+ /* A status in a sysex? */
+ sysex[i] = 0xf7;
+ sd->sysex_state = 0;
+ }
+ break;
+ }
+ c[i] = sysex[i];
+ if (!sd->sysex_state)
+ break;
+ }
+ mtx_unlock(&sd->status_mtx);
+
+ return (md->synth.writeraw(md, c, i, &lenw, 1));
+}
+
+static int
+synth_prefixcmd(mididev_info *md, int status)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_volumemethod(mididev_info *md, int mode)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_readraw(mididev_info *md, u_char *buf, int len, int *lenr, int nonblock)
+{
+ int unit, ret;
+
+ if (md == NULL)
+ return (ENXIO);
+ if (lenr == NULL)
+ return (EINVAL);
+
+ *lenr = 0;
+ unit = md->unit;
+
+ if ((md->fflags & FREAD) == 0) {
+ MIDI_DEBUG(printf("synth_readraw: unit %d is not for reading.\n", unit));
+ return (EIO);
+ }
+
+ mtx_lock(&md->flagqueue_mtx);
+
+ /* Begin recording. */
+ if ((md->flags & MIDI_F_READING) == 0)
+ md->callback(md, MIDI_CB_START | MIDI_CB_RD);
+
+ if (nonblock) {
+ /* Have we got enough data to read? */
+ if (md->midi_dbuf_in.rl < len) {
+ mtx_unlock(&md->flagqueue_mtx);
+ return (EAGAIN);
+ }
+ }
+
+ ret = midibuf_seqread(&md->midi_dbuf_in, buf, len, lenr,
+ md->callback, md, MIDI_CB_START | MIDI_CB_RD,
+ &md->flagqueue_mtx);
+
+ mtx_unlock(&md->flagqueue_mtx);
+
+ return (ret);
+}
+
+static int
+synth_writeraw(mididev_info *md, u_char *buf, int len, int *lenw, int nonblock)
+{
+ int unit, ret;
+
+ if (md == NULL)
+ return (ENXIO);
+ if (lenw == NULL)
+ return (EINVAL);
+
+ *lenw = 0;
+ unit = md->unit;
+
+ if ((md->fflags & FWRITE) == 0) {
+ MIDI_DEBUG(printf("synth_writeraw: unit %d is not for writing.\n", unit));
+ return (EIO);
+ }
+
+ /* For nonblocking, have we got enough space to write? */
+ mtx_lock(&md->flagqueue_mtx);
+ if (nonblock && md->midi_dbuf_out.fl < len) {
+ /* Begin playing. */
+ md->callback(md, MIDI_CB_START | MIDI_CB_WR);
+ mtx_unlock(&md->flagqueue_mtx);
+ return (EAGAIN);
+ }
+
+ ret = midibuf_seqwrite(&md->midi_dbuf_out, buf, len, lenw,
+ md->callback, md, MIDI_CB_START | MIDI_CB_WR,
+ &md->flagqueue_mtx);
+
+ if (ret == 0)
+ /* Begin playing. */
+ md->callback(md, MIDI_CB_START | MIDI_CB_WR);
+
+ mtx_unlock(&md->flagqueue_mtx);
+
+ return (ret);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+static int
+synth_leavesysex(mididev_info *md)
+{
+ int unit, lenw;
+ synthdev_info *sd;
+ u_char c;
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ mtx_lock(&sd->status_mtx);
+ if (!sd->sysex_state) {
+ mtx_unlock(&sd->status_mtx);
+ return (0);
+ }
+
+ sd->sysex_state = 0;
+ mtx_unlock(&sd->status_mtx);
+ c = 0xf7;
+
+ return (md->synth.writeraw(md, &c, sizeof(c), &lenw, 1));
+}
OpenPOWER on IntegriCloud