summaryrefslogtreecommitdiffstats
path: root/sound/synth
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/synth
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'sound/synth')
-rw-r--r--sound/synth/Makefile20
-rw-r--r--sound/synth/emux/Makefile20
-rw-r--r--sound/synth/emux/emux.c173
-rw-r--r--sound/synth/emux/emux_effect.c308
-rw-r--r--sound/synth/emux/emux_hwdep.c171
-rw-r--r--sound/synth/emux/emux_nrpn.c392
-rw-r--r--sound/synth/emux/emux_oss.c497
-rw-r--r--sound/synth/emux/emux_proc.c138
-rw-r--r--sound/synth/emux/emux_seq.c428
-rw-r--r--sound/synth/emux/emux_synth.c963
-rw-r--r--sound/synth/emux/emux_voice.h88
-rw-r--r--sound/synth/emux/soundfont.c1462
-rw-r--r--sound/synth/util_mem.c207
13 files changed, 4867 insertions, 0 deletions
diff --git a/sound/synth/Makefile b/sound/synth/Makefile
new file mode 100644
index 000000000000..986291dcb914
--- /dev/null
+++ b/sound/synth/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-util-mem-objs := util_mem.o
+
+#
+# this function returns:
+# "m" - CONFIG_SND_SEQUENCER is m
+# <empty string> - CONFIG_SND_SEQUENCER is undefined
+# otherwise parameter #1 value
+#
+sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o
+obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o
+obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-util-mem.o
+obj-$(call sequencer,$(CONFIG_SND)) += emux/
diff --git a/sound/synth/emux/Makefile b/sound/synth/emux/Makefile
new file mode 100644
index 000000000000..32a102d26709
--- /dev/null
+++ b/sound/synth/emux/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \
+ emux_effect.o emux_proc.o emux_hwdep.o soundfont.o \
+ $(if $(CONFIG_SND_SEQUENCER_OSS),emux_oss.o)
+
+#
+# this function returns:
+# "m" - CONFIG_SND_SEQUENCER is m
+# <empty string> - CONFIG_SND_SEQUENCER is undefined
+# otherwise parameter #1 value
+#
+sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+
+# Toplevel Module Dependencies
+obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emux-synth.o
+obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-emux-synth.o
diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c
new file mode 100644
index 000000000000..16f3b461627a
--- /dev/null
+++ b/sound/synth/emux/emux.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ * Routines for control of EMU WaveTable chip
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/emux_synth.h>
+#include <linux/init.h>
+#include "emux_voice.h"
+
+MODULE_AUTHOR("Takashi Iwai");
+MODULE_DESCRIPTION("Routines for control of EMU WaveTable chip");
+MODULE_LICENSE("GPL");
+
+/*
+ * create a new hardware dependent device for Emu8000/Emu10k1
+ */
+int snd_emux_new(snd_emux_t **remu)
+{
+ snd_emux_t *emu;
+
+ *remu = NULL;
+ emu = kcalloc(1, sizeof(*emu), GFP_KERNEL);
+ if (emu == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&emu->voice_lock);
+ init_MUTEX(&emu->register_mutex);
+
+ emu->client = -1;
+#ifdef CONFIG_SND_SEQUENCER_OSS
+ emu->oss_synth = NULL;
+#endif
+ emu->max_voices = 0;
+ emu->use_time = 0;
+
+ init_timer(&emu->tlist);
+ emu->tlist.function = snd_emux_timer_callback;
+ emu->tlist.data = (unsigned long)emu;
+ emu->timer_active = 0;
+
+ *remu = emu;
+ return 0;
+}
+
+
+/*
+ */
+int snd_emux_register(snd_emux_t *emu, snd_card_t *card, int index, char *name)
+{
+ int err;
+ snd_sf_callback_t sf_cb;
+
+ snd_assert(emu->hw != NULL, return -EINVAL);
+ snd_assert(emu->max_voices > 0, return -EINVAL);
+ snd_assert(card != NULL, return -EINVAL);
+ snd_assert(name != NULL, return -EINVAL);
+
+ emu->card = card;
+ emu->name = snd_kmalloc_strdup(name, GFP_KERNEL);
+ emu->voices = kcalloc(emu->max_voices, sizeof(snd_emux_voice_t), GFP_KERNEL);
+ if (emu->voices == NULL)
+ return -ENOMEM;
+
+ /* create soundfont list */
+ memset(&sf_cb, 0, sizeof(sf_cb));
+ sf_cb.private_data = emu;
+ sf_cb.sample_new = (snd_sf_sample_new_t)emu->ops.sample_new;
+ sf_cb.sample_free = (snd_sf_sample_free_t)emu->ops.sample_free;
+ sf_cb.sample_reset = (snd_sf_sample_reset_t)emu->ops.sample_reset;
+ emu->sflist = snd_sf_new(&sf_cb, emu->memhdr);
+ if (emu->sflist == NULL)
+ return -ENOMEM;
+
+ if ((err = snd_emux_init_hwdep(emu)) < 0)
+ return err;
+
+ snd_emux_init_voices(emu);
+
+ snd_emux_init_seq(emu, card, index);
+#ifdef CONFIG_SND_SEQUENCER_OSS
+ snd_emux_init_seq_oss(emu);
+#endif
+ snd_emux_init_virmidi(emu, card);
+
+#ifdef CONFIG_PROC_FS
+ snd_emux_proc_init(emu, card, index);
+#endif
+ return 0;
+}
+
+
+/*
+ */
+int snd_emux_free(snd_emux_t *emu)
+{
+ unsigned long flags;
+
+ if (! emu)
+ return -EINVAL;
+
+ spin_lock_irqsave(&emu->voice_lock, flags);
+ if (emu->timer_active)
+ del_timer(&emu->tlist);
+ spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+#ifdef CONFIG_PROC_FS
+ snd_emux_proc_free(emu);
+#endif
+ snd_emux_delete_virmidi(emu);
+#ifdef CONFIG_SND_SEQUENCER_OSS
+ snd_emux_detach_seq_oss(emu);
+#endif
+ snd_emux_detach_seq(emu);
+
+ snd_emux_delete_hwdep(emu);
+
+ if (emu->sflist)
+ snd_sf_free(emu->sflist);
+
+ kfree(emu->voices);
+ kfree(emu->name);
+ kfree(emu);
+ return 0;
+}
+
+
+EXPORT_SYMBOL(snd_emux_new);
+EXPORT_SYMBOL(snd_emux_register);
+EXPORT_SYMBOL(snd_emux_free);
+
+EXPORT_SYMBOL(snd_emux_terminate_all);
+EXPORT_SYMBOL(snd_emux_lock_voice);
+EXPORT_SYMBOL(snd_emux_unlock_voice);
+
+/* soundfont.c */
+EXPORT_SYMBOL(snd_sf_linear_to_log);
+
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_emux_init(void)
+{
+ return 0;
+}
+
+static void __exit alsa_emux_exit(void)
+{
+}
+
+module_init(alsa_emux_init)
+module_exit(alsa_emux_exit)
diff --git a/sound/synth/emux/emux_effect.c b/sound/synth/emux/emux_effect.c
new file mode 100644
index 000000000000..ec3fc1ba7fca
--- /dev/null
+++ b/sound/synth/emux/emux_effect.c
@@ -0,0 +1,308 @@
+/*
+ * Midi synth routines for the Emu8k/Emu10k1
+ *
+ * Copyright (C) 1999 Steve Ratcliffe
+ * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ * Contains code based on awe_wave.c by Takashi Iwai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "emux_voice.h"
+#include <linux/slab.h>
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+/*
+ * effects table
+ */
+
+#define xoffsetof(type,tag) ((long)(&((type)NULL)->tag) - (long)(NULL))
+
+#define parm_offset(tag) xoffsetof(soundfont_voice_parm_t*, tag)
+
+#define PARM_IS_BYTE (1 << 0)
+#define PARM_IS_WORD (1 << 1)
+#define PARM_IS_ALIGNED (3 << 2)
+#define PARM_IS_ALIGN_HI (1 << 2)
+#define PARM_IS_ALIGN_LO (2 << 2)
+#define PARM_IS_SIGNED (1 << 4)
+
+#define PARM_WORD (PARM_IS_WORD)
+#define PARM_BYTE_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO)
+#define PARM_BYTE_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI)
+#define PARM_BYTE (PARM_IS_BYTE)
+#define PARM_SIGN_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO|PARM_IS_SIGNED)
+#define PARM_SIGN_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI|PARM_IS_SIGNED)
+
+static struct emux_parm_defs {
+ int type; /* byte or word */
+ int low, high; /* value range */
+ long offset; /* offset in parameter record (-1 = not written) */
+ int update; /* flgas for real-time update */
+} parm_defs[EMUX_NUM_EFFECTS] = {
+ {PARM_WORD, 0, 0x8000, parm_offset(moddelay), 0}, /* env1 delay */
+ {PARM_BYTE_LO, 1, 0x80, parm_offset(modatkhld), 0}, /* env1 attack */
+ {PARM_BYTE_HI, 0, 0x7e, parm_offset(modatkhld), 0}, /* env1 hold */
+ {PARM_BYTE_LO, 1, 0x7f, parm_offset(moddcysus), 0}, /* env1 decay */
+ {PARM_BYTE_LO, 1, 0x7f, parm_offset(modrelease), 0}, /* env1 release */
+ {PARM_BYTE_HI, 0, 0x7f, parm_offset(moddcysus), 0}, /* env1 sustain */
+ {PARM_BYTE_HI, 0, 0xff, parm_offset(pefe), 0}, /* env1 pitch */
+ {PARM_BYTE_LO, 0, 0xff, parm_offset(pefe), 0}, /* env1 fc */
+
+ {PARM_WORD, 0, 0x8000, parm_offset(voldelay), 0}, /* env2 delay */
+ {PARM_BYTE_LO, 1, 0x80, parm_offset(volatkhld), 0}, /* env2 attack */
+ {PARM_BYTE_HI, 0, 0x7e, parm_offset(volatkhld), 0}, /* env2 hold */
+ {PARM_BYTE_LO, 1, 0x7f, parm_offset(voldcysus), 0}, /* env2 decay */
+ {PARM_BYTE_LO, 1, 0x7f, parm_offset(volrelease), 0}, /* env2 release */
+ {PARM_BYTE_HI, 0, 0x7f, parm_offset(voldcysus), 0}, /* env2 sustain */
+
+ {PARM_WORD, 0, 0x8000, parm_offset(lfo1delay), 0}, /* lfo1 delay */
+ {PARM_BYTE_LO, 0, 0xff, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 freq */
+ {PARM_SIGN_HI, -128, 127, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 vol */
+ {PARM_SIGN_HI, -128, 127, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 pitch */
+ {PARM_BYTE_LO, 0, 0xff, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 cutoff */
+
+ {PARM_WORD, 0, 0x8000, parm_offset(lfo2delay), 0}, /* lfo2 delay */
+ {PARM_BYTE_LO, 0, 0xff, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 freq */
+ {PARM_SIGN_HI, -128, 127, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 pitch */
+
+ {PARM_WORD, 0, 0xffff, -1, SNDRV_EMUX_UPDATE_PITCH}, /* initial pitch */
+ {PARM_BYTE, 0, 0xff, parm_offset(chorus), 0}, /* chorus */
+ {PARM_BYTE, 0, 0xff, parm_offset(reverb), 0}, /* reverb */
+ {PARM_BYTE, 0, 0xff, parm_offset(cutoff), SNDRV_EMUX_UPDATE_VOLUME}, /* cutoff */
+ {PARM_BYTE, 0, 15, parm_offset(filterQ), SNDRV_EMUX_UPDATE_Q}, /* resonance */
+
+ {PARM_WORD, 0, 0xffff, -1, 0}, /* sample start */
+ {PARM_WORD, 0, 0xffff, -1, 0}, /* loop start */
+ {PARM_WORD, 0, 0xffff, -1, 0}, /* loop end */
+ {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse sample start */
+ {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop start */
+ {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop end */
+ {PARM_BYTE, 0, 0xff, -1, SNDRV_EMUX_UPDATE_VOLUME}, /* initial attenuation */
+};
+
+/* set byte effect value */
+static void
+effect_set_byte(unsigned char *valp, snd_midi_channel_t *chan, int type)
+{
+ short effect;
+ snd_emux_effect_table_t *fx = chan->private;
+
+ effect = fx->val[type];
+ if (fx->flag[type] == EMUX_FX_FLAG_ADD) {
+ if (parm_defs[type].type & PARM_IS_SIGNED)
+ effect += *(char*)valp;
+ else
+ effect += *valp;
+ }
+ if (effect < parm_defs[type].low)
+ effect = parm_defs[type].low;
+ else if (effect > parm_defs[type].high)
+ effect = parm_defs[type].high;
+ *valp = (unsigned char)effect;
+}
+
+/* set word effect value */
+static void
+effect_set_word(unsigned short *valp, snd_midi_channel_t *chan, int type)
+{
+ int effect;
+ snd_emux_effect_table_t *fx = chan->private;
+
+ effect = *(unsigned short*)&fx->val[type];
+ if (fx->flag[type] == EMUX_FX_FLAG_ADD)
+ effect += *valp;
+ if (effect < parm_defs[type].low)
+ effect = parm_defs[type].low;
+ else if (effect > parm_defs[type].high)
+ effect = parm_defs[type].high;
+ *valp = (unsigned short)effect;
+}
+
+/* address offset */
+static int
+effect_get_offset(snd_midi_channel_t *chan, int lo, int hi, int mode)
+{
+ int addr = 0;
+ snd_emux_effect_table_t *fx = chan->private;
+
+ if (fx->flag[hi])
+ addr = (short)fx->val[hi];
+ addr = addr << 15;
+ if (fx->flag[lo])
+ addr += (short)fx->val[lo];
+ if (!(mode & SNDRV_SFNT_SAMPLE_8BITS))
+ addr /= 2;
+ return addr;
+}
+
+#ifdef CONFIG_SND_SEQUENCER_OSS
+/* change effects - for OSS sequencer compatibility */
+void
+snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val)
+{
+ int mode;
+
+ if (type & 0x40)
+ mode = EMUX_FX_FLAG_OFF;
+ else if (type & 0x80)
+ mode = EMUX_FX_FLAG_ADD;
+ else
+ mode = EMUX_FX_FLAG_SET;
+ type &= 0x3f;
+
+ snd_emux_send_effect(port, chan, type, val, mode);
+}
+#endif
+
+/* Modify the effect value.
+ * if update is necessary, call emu8000_control
+ */
+void
+snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode)
+{
+ int i;
+ int offset;
+ unsigned char *srcp, *origp;
+ snd_emux_t *emu;
+ snd_emux_effect_table_t *fx;
+ unsigned long flags;
+
+ emu = port->emu;
+ fx = chan->private;
+ if (emu == NULL || fx == NULL)
+ return;
+ if (type < 0 || type >= EMUX_NUM_EFFECTS)
+ return;
+
+ fx->val[type] = val;
+ fx->flag[type] = mode;
+
+ /* do we need to modify the register in realtime ? */
+ if (! parm_defs[type].update || (offset = parm_defs[type].offset) < 0)
+ return;
+
+#ifdef SNDRV_LITTLE_ENDIAN
+ if (parm_defs[type].type & PARM_IS_ALIGN_HI)
+ offset++;
+#else
+ if (parm_defs[type].type & PARM_IS_ALIGN_LO)
+ offset++;
+#endif
+ /* modify the register values */
+ spin_lock_irqsave(&emu->voice_lock, flags);
+ for (i = 0; i < emu->max_voices; i++) {
+ snd_emux_voice_t *vp = &emu->voices[i];
+ if (!STATE_IS_PLAYING(vp->state) || vp->chan != chan)
+ continue;
+ srcp = (unsigned char*)&vp->reg.parm + offset;
+ origp = (unsigned char*)&vp->zone->v.parm + offset;
+ if (parm_defs[i].type & PARM_IS_BYTE) {
+ *srcp = *origp;
+ effect_set_byte(srcp, chan, type);
+ } else {
+ *(unsigned short*)srcp = *(unsigned short*)origp;
+ effect_set_word((unsigned short*)srcp, chan, type);
+ }
+ }
+ spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+ /* activate them */
+ snd_emux_update_channel(port, chan, parm_defs[type].update);
+}
+
+
+/* copy wavetable registers to voice table */
+void
+snd_emux_setup_effect(snd_emux_voice_t *vp)
+{
+ snd_midi_channel_t *chan = vp->chan;
+ snd_emux_effect_table_t *fx;
+ unsigned char *srcp;
+ int i;
+
+ if (! (fx = chan->private))
+ return;
+
+ /* modify the register values via effect table */
+ for (i = 0; i < EMUX_FX_END; i++) {
+ int offset;
+ if (! fx->flag[i] || (offset = parm_defs[i].offset) < 0)
+ continue;
+#ifdef SNDRV_LITTLE_ENDIAN
+ if (parm_defs[i].type & PARM_IS_ALIGN_HI)
+ offset++;
+#else
+ if (parm_defs[i].type & PARM_IS_ALIGN_LO)
+ offset++;
+#endif
+ srcp = (unsigned char*)&vp->reg.parm + offset;
+ if (parm_defs[i].type & PARM_IS_BYTE)
+ effect_set_byte(srcp, chan, i);
+ else
+ effect_set_word((unsigned short*)srcp, chan, i);
+ }
+
+ /* correct sample and loop points */
+ vp->reg.start += effect_get_offset(chan, EMUX_FX_SAMPLE_START,
+ EMUX_FX_COARSE_SAMPLE_START,
+ vp->reg.sample_mode);
+
+ vp->reg.loopstart += effect_get_offset(chan, EMUX_FX_LOOP_START,
+ EMUX_FX_COARSE_LOOP_START,
+ vp->reg.sample_mode);
+
+ vp->reg.loopend += effect_get_offset(chan, EMUX_FX_LOOP_END,
+ EMUX_FX_COARSE_LOOP_END,
+ vp->reg.sample_mode);
+}
+
+/*
+ * effect table
+ */
+void
+snd_emux_create_effect(snd_emux_port_t *p)
+{
+ int i;
+ p->effect = kcalloc(p->chset.max_channels, sizeof(snd_emux_effect_table_t), GFP_KERNEL);
+ if (p->effect) {
+ for (i = 0; i < p->chset.max_channels; i++)
+ p->chset.channels[i].private = p->effect + i;
+ } else {
+ for (i = 0; i < p->chset.max_channels; i++)
+ p->chset.channels[i].private = NULL;
+ }
+}
+
+void
+snd_emux_delete_effect(snd_emux_port_t *p)
+{
+ if (p->effect) {
+ kfree(p->effect);
+ p->effect = NULL;
+ }
+}
+
+void
+snd_emux_clear_effect(snd_emux_port_t *p)
+{
+ if (p->effect) {
+ memset(p->effect, 0, sizeof(snd_emux_effect_table_t) * p->chset.max_channels);
+ }
+}
+
+#endif /* SNDRV_EMUX_USE_RAW_EFFECT */
diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c
new file mode 100644
index 000000000000..4182b44eb47e
--- /dev/null
+++ b/sound/synth/emux/emux_hwdep.c
@@ -0,0 +1,171 @@
+/*
+ * Interface for hwdep device
+ *
+ * Copyright (C) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/hwdep.h>
+#include <asm/uaccess.h>
+#include "emux_voice.h"
+
+/*
+ * open the hwdep device
+ */
+static int
+snd_emux_hwdep_open(snd_hwdep_t *hw, struct file *file)
+{
+ return 0;
+}
+
+
+/*
+ * close the device
+ */
+static int
+snd_emux_hwdep_release(snd_hwdep_t *hw, struct file *file)
+{
+ return 0;
+}
+
+
+#define TMP_CLIENT_ID 0x1001
+
+/*
+ * load patch
+ */
+static int
+snd_emux_hwdep_load_patch(snd_emux_t *emu, void __user *arg)
+{
+ int err;
+ soundfont_patch_info_t patch;
+
+ if (copy_from_user(&patch, arg, sizeof(patch)))
+ return -EFAULT;
+
+ if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
+ patch.type <= SNDRV_SFNT_PROBE_DATA) {
+ err = snd_soundfont_load(emu->sflist, arg, patch.len + sizeof(patch), TMP_CLIENT_ID);
+ if (err < 0)
+ return err;
+ } else {
+ if (emu->ops.load_fx)
+ return emu->ops.load_fx(emu, patch.type, patch.optarg, arg, patch.len + sizeof(patch));
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * set misc mode
+ */
+static int
+snd_emux_hwdep_misc_mode(snd_emux_t *emu, void __user *arg)
+{
+ struct sndrv_emux_misc_mode info;
+ int i;
+
+ if (copy_from_user(&info, arg, sizeof(info)))
+ return -EFAULT;
+ if (info.mode < 0 || info.mode >= EMUX_MD_END)
+ return -EINVAL;
+
+ if (info.port < 0) {
+ for (i = 0; i < emu->num_ports; i++)
+ emu->portptrs[i]->ctrls[info.mode] = info.value;
+ } else {
+ if (info.port < emu->num_ports)
+ emu->portptrs[info.port]->ctrls[info.mode] = info.value;
+ }
+ return 0;
+}
+
+
+/*
+ * ioctl
+ */
+static int
+snd_emux_hwdep_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ snd_emux_t *emu = hw->private_data;
+
+ switch (cmd) {
+ case SNDRV_EMUX_IOCTL_VERSION:
+ return put_user(SNDRV_EMUX_VERSION, (unsigned int __user *)arg);
+ case SNDRV_EMUX_IOCTL_LOAD_PATCH:
+ return snd_emux_hwdep_load_patch(emu, (void __user *)arg);
+ case SNDRV_EMUX_IOCTL_RESET_SAMPLES:
+ snd_soundfont_remove_samples(emu->sflist);
+ break;
+ case SNDRV_EMUX_IOCTL_REMOVE_LAST_SAMPLES:
+ snd_soundfont_remove_unlocked(emu->sflist);
+ break;
+ case SNDRV_EMUX_IOCTL_MEM_AVAIL:
+ if (emu->memhdr) {
+ int size = snd_util_mem_avail(emu->memhdr);
+ return put_user(size, (unsigned int __user *)arg);
+ }
+ break;
+ case SNDRV_EMUX_IOCTL_MISC_MODE:
+ return snd_emux_hwdep_misc_mode(emu, (void __user *)arg);
+ }
+
+ return 0;
+}
+
+
+/*
+ * register hwdep device
+ */
+
+int
+snd_emux_init_hwdep(snd_emux_t *emu)
+{
+ snd_hwdep_t *hw;
+ int err;
+
+ if ((err = snd_hwdep_new(emu->card, SNDRV_EMUX_HWDEP_NAME, emu->hwdep_idx, &hw)) < 0)
+ return err;
+ emu->hwdep = hw;
+ strcpy(hw->name, SNDRV_EMUX_HWDEP_NAME);
+ hw->iface = SNDRV_HWDEP_IFACE_EMUX_WAVETABLE;
+ hw->ops.open = snd_emux_hwdep_open;
+ hw->ops.release = snd_emux_hwdep_release;
+ hw->ops.ioctl = snd_emux_hwdep_ioctl;
+ hw->exclusive = 1;
+ hw->private_data = emu;
+ if ((err = snd_card_register(emu->card)) < 0)
+ return err;
+
+ return 0;
+}
+
+
+/*
+ * unregister
+ */
+void
+snd_emux_delete_hwdep(snd_emux_t *emu)
+{
+ if (emu->hwdep) {
+ snd_device_free(emu->card, emu->hwdep);
+ emu->hwdep = NULL;
+ }
+}
diff --git a/sound/synth/emux/emux_nrpn.c b/sound/synth/emux/emux_nrpn.c
new file mode 100644
index 000000000000..25edff9e1fc2
--- /dev/null
+++ b/sound/synth/emux/emux_nrpn.c
@@ -0,0 +1,392 @@
+/*
+ * NRPN / SYSEX callbacks for Emu8k/Emu10k1
+ *
+ * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "emux_voice.h"
+#include <sound/asoundef.h>
+
+/*
+ * conversion from NRPN/control parameters to Emu8000 raw parameters
+ */
+
+/* NRPN / CC -> Emu8000 parameter converter */
+typedef struct {
+ int control;
+ int effect;
+ int (*convert)(int val);
+} nrpn_conv_table;
+
+/* effect sensitivity */
+
+#define FX_CUTOFF 0
+#define FX_RESONANCE 1
+#define FX_ATTACK 2
+#define FX_RELEASE 3
+#define FX_VIBRATE 4
+#define FX_VIBDEPTH 5
+#define FX_VIBDELAY 6
+#define FX_NUMS 7
+
+/*
+ * convert NRPN/control values
+ */
+
+static int send_converted_effect(nrpn_conv_table *table, int num_tables,
+ snd_emux_port_t *port, snd_midi_channel_t *chan,
+ int type, int val, int mode)
+{
+ int i, cval;
+ for (i = 0; i < num_tables; i++) {
+ if (table[i].control == type) {
+ cval = table[i].convert(val);
+ snd_emux_send_effect(port, chan, table[i].effect,
+ cval, mode);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#define DEF_FX_CUTOFF 170
+#define DEF_FX_RESONANCE 6
+#define DEF_FX_ATTACK 50
+#define DEF_FX_RELEASE 50
+#define DEF_FX_VIBRATE 30
+#define DEF_FX_VIBDEPTH 4
+#define DEF_FX_VIBDELAY 1500
+
+/* effect sensitivities for GS NRPN:
+ * adjusted for chaos 8MB soundfonts
+ */
+static int gs_sense[] =
+{
+ DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
+ DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
+};
+
+/* effect sensitivies for XG controls:
+ * adjusted for chaos 8MB soundfonts
+ */
+static int xg_sense[] =
+{
+ DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE,
+ DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY
+};
+
+
+/*
+ * AWE32 NRPN effects
+ */
+
+static int fx_delay(int val);
+static int fx_attack(int val);
+static int fx_hold(int val);
+static int fx_decay(int val);
+static int fx_the_value(int val);
+static int fx_twice_value(int val);
+static int fx_conv_pitch(int val);
+static int fx_conv_Q(int val);
+
+/* function for each NRPN */ /* [range] units */
+#define fx_env1_delay fx_delay /* [0,5900] 4msec */
+#define fx_env1_attack fx_attack /* [0,5940] 1msec */
+#define fx_env1_hold fx_hold /* [0,8191] 1msec */
+#define fx_env1_decay fx_decay /* [0,5940] 4msec */
+#define fx_env1_release fx_decay /* [0,5940] 4msec */
+#define fx_env1_sustain fx_the_value /* [0,127] 0.75dB */
+#define fx_env1_pitch fx_the_value /* [-127,127] 9.375cents */
+#define fx_env1_cutoff fx_the_value /* [-127,127] 56.25cents */
+
+#define fx_env2_delay fx_delay /* [0,5900] 4msec */
+#define fx_env2_attack fx_attack /* [0,5940] 1msec */
+#define fx_env2_hold fx_hold /* [0,8191] 1msec */
+#define fx_env2_decay fx_decay /* [0,5940] 4msec */
+#define fx_env2_release fx_decay /* [0,5940] 4msec */
+#define fx_env2_sustain fx_the_value /* [0,127] 0.75dB */
+
+#define fx_lfo1_delay fx_delay /* [0,5900] 4msec */
+#define fx_lfo1_freq fx_twice_value /* [0,127] 84mHz */
+#define fx_lfo1_volume fx_twice_value /* [0,127] 0.1875dB */
+#define fx_lfo1_pitch fx_the_value /* [-127,127] 9.375cents */
+#define fx_lfo1_cutoff fx_twice_value /* [-64,63] 56.25cents */
+
+#define fx_lfo2_delay fx_delay /* [0,5900] 4msec */
+#define fx_lfo2_freq fx_twice_value /* [0,127] 84mHz */
+#define fx_lfo2_pitch fx_the_value /* [-127,127] 9.375cents */
+
+#define fx_init_pitch fx_conv_pitch /* [-8192,8192] cents */
+#define fx_chorus fx_the_value /* [0,255] -- */
+#define fx_reverb fx_the_value /* [0,255] -- */
+#define fx_cutoff fx_twice_value /* [0,127] 62Hz */
+#define fx_filterQ fx_conv_Q /* [0,127] -- */
+
+static int fx_delay(int val)
+{
+ return (unsigned short)snd_sf_calc_parm_delay(val);
+}
+
+static int fx_attack(int val)
+{
+ return (unsigned short)snd_sf_calc_parm_attack(val);
+}
+
+static int fx_hold(int val)
+{
+ return (unsigned short)snd_sf_calc_parm_hold(val);
+}
+
+static int fx_decay(int val)
+{
+ return (unsigned short)snd_sf_calc_parm_decay(val);
+}
+
+static int fx_the_value(int val)
+{
+ return (unsigned short)(val & 0xff);
+}
+
+static int fx_twice_value(int val)
+{
+ return (unsigned short)((val * 2) & 0xff);
+}
+
+static int fx_conv_pitch(int val)
+{
+ return (short)(val * 4096 / 1200);
+}
+
+static int fx_conv_Q(int val)
+{
+ return (unsigned short)((val / 8) & 0xff);
+}
+
+
+static nrpn_conv_table awe_effects[] =
+{
+ { 0, EMUX_FX_LFO1_DELAY, fx_lfo1_delay},
+ { 1, EMUX_FX_LFO1_FREQ, fx_lfo1_freq},
+ { 2, EMUX_FX_LFO2_DELAY, fx_lfo2_delay},
+ { 3, EMUX_FX_LFO2_FREQ, fx_lfo2_freq},
+
+ { 4, EMUX_FX_ENV1_DELAY, fx_env1_delay},
+ { 5, EMUX_FX_ENV1_ATTACK,fx_env1_attack},
+ { 6, EMUX_FX_ENV1_HOLD, fx_env1_hold},
+ { 7, EMUX_FX_ENV1_DECAY, fx_env1_decay},
+ { 8, EMUX_FX_ENV1_SUSTAIN, fx_env1_sustain},
+ { 9, EMUX_FX_ENV1_RELEASE, fx_env1_release},
+
+ {10, EMUX_FX_ENV2_DELAY, fx_env2_delay},
+ {11, EMUX_FX_ENV2_ATTACK, fx_env2_attack},
+ {12, EMUX_FX_ENV2_HOLD, fx_env2_hold},
+ {13, EMUX_FX_ENV2_DECAY, fx_env2_decay},
+ {14, EMUX_FX_ENV2_SUSTAIN, fx_env2_sustain},
+ {15, EMUX_FX_ENV2_RELEASE, fx_env2_release},
+
+ {16, EMUX_FX_INIT_PITCH, fx_init_pitch},
+ {17, EMUX_FX_LFO1_PITCH, fx_lfo1_pitch},
+ {18, EMUX_FX_LFO2_PITCH, fx_lfo2_pitch},
+ {19, EMUX_FX_ENV1_PITCH, fx_env1_pitch},
+ {20, EMUX_FX_LFO1_VOLUME, fx_lfo1_volume},
+ {21, EMUX_FX_CUTOFF, fx_cutoff},
+ {22, EMUX_FX_FILTERQ, fx_filterQ},
+ {23, EMUX_FX_LFO1_CUTOFF, fx_lfo1_cutoff},
+ {24, EMUX_FX_ENV1_CUTOFF, fx_env1_cutoff},
+ {25, EMUX_FX_CHORUS, fx_chorus},
+ {26, EMUX_FX_REVERB, fx_reverb},
+};
+
+
+/*
+ * GS(SC88) NRPN effects; still experimental
+ */
+
+/* cutoff: quarter semitone step, max=255 */
+static int gs_cutoff(int val)
+{
+ return (val - 64) * gs_sense[FX_CUTOFF] / 50;
+}
+
+/* resonance: 0 to 15(max) */
+static int gs_filterQ(int val)
+{
+ return (val - 64) * gs_sense[FX_RESONANCE] / 50;
+}
+
+/* attack: */
+static int gs_attack(int val)
+{
+ return -(val - 64) * gs_sense[FX_ATTACK] / 50;
+}
+
+/* decay: */
+static int gs_decay(int val)
+{
+ return -(val - 64) * gs_sense[FX_RELEASE] / 50;
+}
+
+/* release: */
+static int gs_release(int val)
+{
+ return -(val - 64) * gs_sense[FX_RELEASE] / 50;
+}
+
+/* vibrato freq: 0.042Hz step, max=255 */
+static int gs_vib_rate(int val)
+{
+ return (val - 64) * gs_sense[FX_VIBRATE] / 50;
+}
+
+/* vibrato depth: max=127, 1 octave */
+static int gs_vib_depth(int val)
+{
+ return (val - 64) * gs_sense[FX_VIBDEPTH] / 50;
+}
+
+/* vibrato delay: -0.725msec step */
+static int gs_vib_delay(int val)
+{
+ return -(val - 64) * gs_sense[FX_VIBDELAY] / 50;
+}
+
+static nrpn_conv_table gs_effects[] =
+{
+ {32, EMUX_FX_CUTOFF, gs_cutoff},
+ {33, EMUX_FX_FILTERQ, gs_filterQ},
+ {99, EMUX_FX_ENV2_ATTACK, gs_attack},
+ {100, EMUX_FX_ENV2_DECAY, gs_decay},
+ {102, EMUX_FX_ENV2_RELEASE, gs_release},
+ {8, EMUX_FX_LFO1_FREQ, gs_vib_rate},
+ {9, EMUX_FX_LFO1_VOLUME, gs_vib_depth},
+ {10, EMUX_FX_LFO1_DELAY, gs_vib_delay},
+};
+
+
+/*
+ * NRPN events
+ */
+void
+snd_emux_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset)
+{
+ snd_emux_port_t *port;
+
+ port = p;
+ snd_assert(port != NULL, return);
+ snd_assert(chan != NULL, return);
+
+ if (chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 127 &&
+ chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] <= 26) {
+ int val;
+ /* Win/DOS AWE32 specific NRPNs */
+ /* both MSB/LSB necessary */
+ val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) |
+ chan->control[MIDI_CTL_LSB_DATA_ENTRY];
+ val -= 8192;
+ send_converted_effect
+ (awe_effects, ARRAY_SIZE(awe_effects),
+ port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB],
+ val, EMUX_FX_FLAG_SET);
+ return;
+ }
+
+ if (port->chset.midi_mode == SNDRV_MIDI_MODE_GS &&
+ chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 1) {
+ int val;
+ /* GS specific NRPNs */
+ /* only MSB is valid */
+ val = chan->control[MIDI_CTL_MSB_DATA_ENTRY];
+ send_converted_effect
+ (gs_effects, ARRAY_SIZE(gs_effects),
+ port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB],
+ val, EMUX_FX_FLAG_ADD);
+ return;
+ }
+}
+
+
+/*
+ * XG control effects; still experimental
+ */
+
+/* cutoff: quarter semitone step, max=255 */
+static int xg_cut