db90128160
cloning infrastructure standard in kern_conf. Modules are now the same with or without devfs support. If you need to detect if devfs is present, in modules or elsewhere, check the integer variable "devfs_present". This happily removes an ugly hack from kern/vfs_conf.c. This forces a rename of the eventhandler and the standard clone helper function. Include <sys/eventhandler.h> in <sys/conf.h>: it's a helper #include like <sys/queue.h> Remove all #includes of opt_devfs.h they no longer matter.
631 lines
13 KiB
C
631 lines
13 KiB
C
/*
|
|
* 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 <stddef.h>
|
|
#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 sys/i386/isa/snd/sequencer.c.
|
|
*/
|
|
|
|
static int
|
|
synth_killnote(mididev_info *md, int chn, int note, int vel)
|
|
{
|
|
int unit, msg, chp;
|
|
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);
|
|
|
|
msg = sd->prev_out_status & 0xf0;
|
|
chp = sd->prev_out_status & 0x0f;
|
|
|
|
if (chp == chn && ((msg == 0x90 && vel == 64) || msg == 0x80)) {
|
|
/* Use running status. */
|
|
c[0] = (u_char)note;
|
|
if (msg == 0x90)
|
|
/* The note was on. */
|
|
c[1] = 0;
|
|
else
|
|
c[1] = (u_char)vel;
|
|
|
|
if (synth_prefixcmd(md, c[0]))
|
|
return (0);
|
|
if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
} else {
|
|
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);
|
|
if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
|
|
return EAGAIN;
|
|
/* Update the status. */
|
|
sd->prev_out_status = c[0];
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
synth_setinstr(mididev_info *md, int chn, int instr)
|
|
{
|
|
int unit;
|
|
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;
|
|
if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
/* Update the status. */
|
|
sd->prev_out_status = c[0];
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
synth_startnote(mididev_info *md, int chn, int note, int vel)
|
|
{
|
|
int unit, msg, chp;
|
|
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);
|
|
|
|
msg = sd->prev_out_status & 0xf0;
|
|
chp = sd->prev_out_status & 0x0f;
|
|
|
|
if (chp == chn && msg == 0x90) {
|
|
/* Use running status. */
|
|
c[0] = (u_char)note;
|
|
c[1] = (u_char)vel;
|
|
if (synth_prefixcmd(md, c[0]))
|
|
return (0);
|
|
if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
} else {
|
|
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);
|
|
if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
/* Update the status. */
|
|
sd->prev_out_status = c[0];
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
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;
|
|
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, 0);
|
|
/* Update the status. */
|
|
sd->prev_out_status = c[i];
|
|
return (0);
|
|
}
|
|
first_byte = 0;
|
|
}
|
|
|
|
if (!eox_seen) {
|
|
c[0] = 0xf7;
|
|
md->synth.writeraw(md, c, 1, 0);
|
|
sd->prev_out_status = c[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, msg, chp;
|
|
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);
|
|
|
|
msg = sd->prev_out_status & 0xf0;
|
|
chp = sd->prev_out_status & 0x0f;
|
|
|
|
if (chp == chn && msg == 0xd0) {
|
|
/* Use running status. */
|
|
c[0] = (u_char)press;
|
|
if (synth_prefixcmd(md, c[0]))
|
|
return (0);
|
|
if (md->synth.writeraw(md, c, 1, 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
} else {
|
|
c[0] = 0xd0 | (chn & 0x0f); /* Channel Pressure. */
|
|
c[1] = (u_char)press;
|
|
if (synth_prefixcmd(md, c[0]))
|
|
return (0);
|
|
if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
/* Update the status. */
|
|
sd->prev_out_status = c[0];
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
synth_controller(mididev_info *md, int chn, int ctrlnum, int val)
|
|
{
|
|
int unit, msg, chp;
|
|
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);
|
|
|
|
msg = sd->prev_out_status & 0xf0;
|
|
chp = sd->prev_out_status & 0x0f;
|
|
|
|
if (chp == chn && msg == 0xb0) {
|
|
/* Use running status. */
|
|
c[0] = (u_char)ctrlnum;
|
|
c[1] = (u_char)val & 0x7f;
|
|
if (synth_prefixcmd(md, c[0]))
|
|
return (0);
|
|
if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
} else {
|
|
c[0] = 0xb0 | (chn & 0x0f); /* Control Message. */
|
|
c[1] = (u_char)ctrlnum;
|
|
if (synth_prefixcmd(md, c[0]))
|
|
return (0);
|
|
if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
/* Update the status. */
|
|
sd->prev_out_status = c[0];
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
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, msg, chp;
|
|
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);
|
|
|
|
msg = sd->prev_out_status & 0xf0;
|
|
chp = sd->prev_out_status & 0x0f;
|
|
|
|
if (chp == chn && msg == 0xe0) {
|
|
/* Use running status. */
|
|
c[0] = (u_char)val & 0x7f;
|
|
c[1] = (u_char)(val >> 7) & 0x7f;
|
|
if (synth_prefixcmd(md, c[0]))
|
|
return (0);
|
|
if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
} else {
|
|
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);
|
|
if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
/* Update the status. */
|
|
sd->prev_out_status = c[0];
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
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, j;
|
|
synthdev_info *sd;
|
|
u_char c[len];
|
|
|
|
unit = md->unit;
|
|
sd = &md->synth;
|
|
|
|
for (i = 0 ; i < len ; i++) {
|
|
switch (sysex[i]) {
|
|
case 0xf0:
|
|
/* Sysex begins. */
|
|
if (synth_prefixcmd(md, 0xf0))
|
|
return (0);
|
|
sd->sysex_state = 1;
|
|
break;
|
|
case 0xf7:
|
|
/* Sysex ends. */
|
|
if (!sd->sysex_state)
|
|
return (0);
|
|
sd->sysex_state = 0;
|
|
break;
|
|
default:
|
|
if (!sd->sysex_state)
|
|
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;
|
|
}
|
|
if (md->synth.writeraw(md, c, i, 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
|
|
/* Update the status. */
|
|
for (j = i - 1 ; j >= 0 ; j--)
|
|
if ((c[j] & 0x80) != 0) {
|
|
sd->prev_out_status = c[j];
|
|
break;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
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 nonblock)
|
|
{
|
|
int unit, ret, s;
|
|
|
|
if (md == NULL)
|
|
return (ENXIO);
|
|
|
|
unit = md->unit;
|
|
if (unit >= nmidi + nsynth) {
|
|
DEB(printf("synth_readraw: unit %d does not exist.\n", unit));
|
|
return (ENXIO);
|
|
}
|
|
if ((md->fflags & FREAD) == 0) {
|
|
DEB(printf("mpu_readraw: unit %d is not for reading.\n", unit));
|
|
return (EIO);
|
|
}
|
|
|
|
s = splmidi();
|
|
|
|
/* Begin recording. */
|
|
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)
|
|
return (EAGAIN);
|
|
}
|
|
|
|
ret = midibuf_seqread(&md->midi_dbuf_in, buf, len);
|
|
|
|
splx(s);
|
|
|
|
if (ret < 0)
|
|
ret = -ret;
|
|
else
|
|
ret = 0;
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
synth_writeraw(mididev_info *md, u_char *buf, int len, int nonblock)
|
|
{
|
|
int unit, ret, s;
|
|
|
|
if (md == NULL)
|
|
return (ENXIO);
|
|
|
|
unit = md->unit;
|
|
|
|
if (unit >= nmidi + nsynth) {
|
|
DEB(printf("synth_writeraw: unit %d does not exist.\n", unit));
|
|
return (ENXIO);
|
|
}
|
|
if ((md->fflags & FWRITE) == 0) {
|
|
DEB(printf("synth_writeraw: unit %d is not for writing.\n", unit));
|
|
return (EIO);
|
|
}
|
|
|
|
/* For nonblocking, have we got enough space to write? */
|
|
if (nonblock && md->midi_dbuf_out.fl < len)
|
|
return (EAGAIN);
|
|
|
|
s = splmidi();
|
|
|
|
ret = midibuf_seqwrite(&md->midi_dbuf_out, buf, len);
|
|
if (ret < 0)
|
|
ret = -ret;
|
|
else
|
|
ret = 0;
|
|
|
|
/* Begin playing. */
|
|
md->callback(md, MIDI_CB_START | MIDI_CB_WR);
|
|
|
|
splx(s);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* The functions below here are the libraries for the above ones.
|
|
*/
|
|
|
|
static int
|
|
synth_leavesysex(mididev_info *md)
|
|
{
|
|
int unit;
|
|
synthdev_info *sd;
|
|
u_char c;
|
|
|
|
unit = md->unit;
|
|
sd = &md->synth;
|
|
|
|
if (!sd->sysex_state)
|
|
return (0);
|
|
|
|
sd->sysex_state = 0;
|
|
c = 0xf7;
|
|
if (md->synth.writeraw(md, &c, sizeof(c), 1) == EAGAIN)
|
|
return (EAGAIN);
|
|
sd->sysex_state = 0;
|
|
/* Update the status. */
|
|
sd->prev_out_status = c;
|
|
|
|
return (0);
|
|
}
|