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!
diff --git a/sound/synth/emux/Makefile b/sound/synth/emux/Makefile
new file mode 100644
index 0000000..32a102d
--- /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 0000000..16f3b46
--- /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 0000000..ec3fc1b
--- /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 0000000..4182b44
--- /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 0000000..25edff9
--- /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_cutoff(int val)
+{
+	return (val - 64) * xg_sense[FX_CUTOFF] / 64;
+}
+
+/* resonance: 0(open) to 15(most nasal) */
+static int xg_filterQ(int val)
+{
+	return (val - 64) * xg_sense[FX_RESONANCE] / 64;
+}
+
+/* attack: */
+static int xg_attack(int val)
+{
+	return -(val - 64) * xg_sense[FX_ATTACK] / 64;
+}
+
+/* release: */
+static int xg_release(int val)
+{
+	return -(val - 64) * xg_sense[FX_RELEASE] / 64;
+}
+
+static nrpn_conv_table xg_effects[] =
+{
+	{71, EMUX_FX_CUTOFF,	xg_cutoff},
+	{74, EMUX_FX_FILTERQ,	xg_filterQ},
+	{72, EMUX_FX_ENV2_RELEASE, xg_release},
+	{73, EMUX_FX_ENV2_ATTACK, xg_attack},
+};
+
+int
+snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param)
+{
+	return send_converted_effect(xg_effects, ARRAY_SIZE(xg_effects),
+				     port, chan, param,
+				     chan->control[param],
+				     EMUX_FX_FLAG_ADD);
+}
+
+/*
+ * receive sysex
+ */
+void
+snd_emux_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset)
+{
+	snd_emux_port_t *port;
+	snd_emux_t *emu;
+
+	port = p;
+	snd_assert(port != NULL, return);
+	snd_assert(chset != NULL, return);
+	emu = port->emu;
+
+	switch (parsed) {
+	case SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME:
+		snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME);
+		break;
+	default:
+		if (emu->ops.sysex)
+			emu->ops.sysex(emu, buf, len, parsed, chset);
+		break;
+	}
+}
+
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
new file mode 100644
index 0000000..5272f4a
--- /dev/null
+++ b/sound/synth/emux/emux_oss.c
@@ -0,0 +1,497 @@
+/*
+ *  Interface for OSS sequencer emulation
+ *
+ *  Copyright (C) 1999 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
+ *
+ * Changes
+ * 19990227   Steve Ratcliffe   Made separate file and merged in latest
+ * 				midi emulation.
+ */
+
+#include <sound/driver.h>
+
+#ifdef CONFIG_SND_SEQUENCER_OSS
+
+#include <asm/uaccess.h>
+#include <sound/core.h>
+#include "emux_voice.h"
+#include <sound/asoundef.h>
+
+static int snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure);
+static int snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg);
+static int snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg);
+static int snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, const char __user *buf, int offs, int count);
+static int snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg);
+static int snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop);
+static void reset_port_mode(snd_emux_port_t *port, int midi_mode);
+static void emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop);
+static void gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop);
+static void fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop);
+
+/* operators */
+static snd_seq_oss_callback_t oss_callback = {
+	.owner = THIS_MODULE,
+	.open = snd_emux_open_seq_oss,
+	.close = snd_emux_close_seq_oss,
+	.ioctl = snd_emux_ioctl_seq_oss,
+	.load_patch = snd_emux_load_patch_seq_oss,
+	.reset = snd_emux_reset_seq_oss,
+};
+
+
+/*
+ * register OSS synth
+ */
+
+void
+snd_emux_init_seq_oss(snd_emux_t *emu)
+{
+	snd_seq_oss_reg_t *arg;
+	snd_seq_device_t *dev;
+
+	if (snd_seq_device_new(emu->card, 0, SNDRV_SEQ_DEV_ID_OSS,
+			       sizeof(snd_seq_oss_reg_t), &dev) < 0)
+		return;
+
+	emu->oss_synth = dev;
+	strcpy(dev->name, emu->name);
+	arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	arg->type = SYNTH_TYPE_SAMPLE;
+	arg->subtype = SAMPLE_TYPE_AWE32;
+	arg->nvoices = emu->max_voices;
+	arg->oper = oss_callback;
+	arg->private_data = emu;
+
+	/* register to OSS synth table */
+	snd_device_register(emu->card, dev);
+}
+
+
+/*
+ * unregister
+ */
+void
+snd_emux_detach_seq_oss(snd_emux_t *emu)
+{
+	if (emu->oss_synth) {
+		snd_device_free(emu->card, emu->oss_synth);
+		emu->oss_synth = NULL;
+	}
+}
+
+
+/* use port number as a unique soundfont client number */
+#define SF_CLIENT_NO(p)	((p) + 0x1000)
+
+/*
+ * open port for OSS sequencer
+ */
+static int
+snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure)
+{
+	snd_emux_t *emu;
+	snd_emux_port_t *p;
+	snd_seq_port_callback_t callback;
+	char tmpname[64];
+
+	emu = closure;
+	snd_assert(arg != NULL && emu != NULL, return -ENXIO);
+
+	down(&emu->register_mutex);
+
+	if (!snd_emux_inc_count(emu)) {
+		up(&emu->register_mutex);
+		return -EFAULT;
+	}
+
+	memset(&callback, 0, sizeof(callback));
+	callback.owner = THIS_MODULE;
+	callback.event_input = snd_emux_event_oss_input;
+
+	sprintf(tmpname, "%s OSS Port", emu->name);
+	p = snd_emux_create_port(emu, tmpname, 32,
+				 1, &callback);
+	if (p == NULL) {
+		snd_printk("can't create port\n");
+		snd_emux_dec_count(emu);
+		up(&emu->register_mutex);
+		return -ENOMEM;
+	}
+
+	/* fill the argument data */
+	arg->private_data = p;
+	arg->addr.client = p->chset.client;
+	arg->addr.port = p->chset.port;
+	p->oss_arg = arg;
+
+	reset_port_mode(p, arg->seq_mode);
+
+	snd_emux_reset_port(p);
+
+	up(&emu->register_mutex);
+	return 0;
+}
+
+
+#define DEFAULT_DRUM_FLAGS	((1<<9) | (1<<25))
+
+/*
+ * reset port mode
+ */
+static void
+reset_port_mode(snd_emux_port_t *port, int midi_mode)
+{
+	if (midi_mode) {
+		port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_MIDI;
+		port->drum_flags = DEFAULT_DRUM_FLAGS;
+		port->volume_atten = 0;
+		port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_KEYPRESS;
+	} else {
+		port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_SYNTH;
+		port->drum_flags = 0;
+		port->volume_atten = 32;
+		port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS;
+	}
+}
+
+
+/*
+ * close port
+ */
+static int
+snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg)
+{
+	snd_emux_t *emu;
+	snd_emux_port_t *p;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	p = arg->private_data;
+	snd_assert(p != NULL, return -ENXIO);
+
+	emu = p->emu;
+	snd_assert(emu != NULL, return -ENXIO);
+
+	down(&emu->register_mutex);
+	snd_emux_sounds_off_all(p);
+	snd_soundfont_close_check(emu->sflist, SF_CLIENT_NO(p->chset.port));
+	snd_seq_event_port_detach(p->chset.client, p->chset.port);
+	snd_emux_dec_count(emu);
+
+	up(&emu->register_mutex);
+	return 0;
+}
+
+
+/*
+ * load patch
+ */
+static int
+snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format,
+			    const char __user *buf, int offs, int count)
+{
+	snd_emux_t *emu;
+	snd_emux_port_t *p;
+	int rc;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	p = arg->private_data;
+	snd_assert(p != NULL, return -ENXIO);
+
+	emu = p->emu;
+	snd_assert(emu != NULL, return -ENXIO);
+
+	if (format == GUS_PATCH)
+		rc = snd_soundfont_load_guspatch(emu->sflist, buf, count,
+						 SF_CLIENT_NO(p->chset.port));
+	else if (format == SNDRV_OSS_SOUNDFONT_PATCH) {
+		soundfont_patch_info_t patch;
+		if (count < (int)sizeof(patch))
+			rc = -EINVAL;
+		if (copy_from_user(&patch, buf, sizeof(patch)))
+			rc = -EFAULT;
+		if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
+		    patch.type <= SNDRV_SFNT_PROBE_DATA)
+			rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port));
+		else {
+			if (emu->ops.load_fx)
+				rc = emu->ops.load_fx(emu, patch.type, patch.optarg, buf, count);
+			else
+				rc = -EINVAL;
+		}
+	} else
+		rc = 0;
+	return rc;
+}
+
+
+/*
+ * ioctl
+ */
+static int
+snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg)
+{
+	snd_emux_port_t *p;
+	snd_emux_t *emu;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	p = arg->private_data;
+	snd_assert(p != NULL, return -ENXIO);
+
+	emu = p->emu;
+	snd_assert(emu != NULL, return -ENXIO);
+
+	switch (cmd) {
+	case SNDCTL_SEQ_RESETSAMPLES:
+		snd_soundfont_remove_samples(emu->sflist);
+		return 0;
+			
+	case SNDCTL_SYNTH_MEMAVL:
+		if (emu->memhdr)
+			return snd_util_mem_avail(emu->memhdr);
+		return 0;
+	}
+
+	return 0;
+}
+
+
+/*
+ * reset device
+ */
+static int
+snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg)
+{
+	snd_emux_port_t *p;
+
+	snd_assert(arg != NULL, return -ENXIO);
+	p = arg->private_data;
+	snd_assert(p != NULL, return -ENXIO);
+	snd_emux_reset_port(p);
+	return 0;
+}
+
+
+/*
+ * receive raw events: only SEQ_PRIVATE is accepted.
+ */
+static int
+snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private_data,
+			 int atomic, int hop)
+{
+	snd_emux_t *emu;
+	snd_emux_port_t *p;
+	unsigned char cmd, *data;
+
+	p = private_data;
+	snd_assert(p != NULL, return -EINVAL);
+	emu = p->emu;
+	snd_assert(emu != NULL, return -EINVAL);
+	if (ev->type != SNDRV_SEQ_EVENT_OSS)
+		return snd_emux_event_input(ev, direct, private_data, atomic, hop);
+
+	data = ev->data.raw8.d;
+	/* only SEQ_PRIVATE is accepted */
+	if (data[0] != 0xfe)
+		return 0;
+	cmd = data[2] & _EMUX_OSS_MODE_VALUE_MASK;
+	if (data[2] & _EMUX_OSS_MODE_FLAG)
+		emuspec_control(emu, p, cmd, data, atomic, hop);
+	else
+		gusspec_control(emu, p, cmd, data, atomic, hop);
+	return 0;
+}
+
+
+/*
+ * OSS/AWE driver specific h/w controls
+ */
+static void
+emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd,
+		unsigned char *event, int atomic, int hop)
+{
+	int voice;
+	unsigned short p1;
+	short p2;
+	int i;
+	snd_midi_channel_t *chan;
+
+	voice = event[3];
+	if (voice < 0 || voice >= port->chset.max_channels)
+		chan = NULL;
+	else
+		chan = &port->chset.channels[voice];
+
+	p1 = *(unsigned short *) &event[4];
+	p2 = *(short *) &event[6];
+
+	switch (cmd) {
+#if 0 /* don't do this atomically */
+	case _EMUX_OSS_REMOVE_LAST_SAMPLES:
+		snd_soundfont_remove_unlocked(emu->sflist);
+		break;
+#endif
+	case _EMUX_OSS_SEND_EFFECT:
+		if (chan)
+			snd_emux_send_effect_oss(port, chan, p1, p2);
+		break;
+		
+	case _EMUX_OSS_TERMINATE_ALL:
+		snd_emux_terminate_all(emu);
+		break;
+
+	case _EMUX_OSS_TERMINATE_CHANNEL:
+		/*snd_emux_mute_channel(emu, chan);*/
+		break;
+	case _EMUX_OSS_RESET_CHANNEL:
+		/*snd_emux_channel_init(chset, chan);*/
+		break;
+
+	case _EMUX_OSS_RELEASE_ALL:
+		fake_event(emu, port, voice, MIDI_CTL_ALL_NOTES_OFF, 0, atomic, hop);
+		break;
+	case _EMUX_OSS_NOTEOFF_ALL:
+		fake_event(emu, port, voice, MIDI_CTL_ALL_SOUNDS_OFF, 0, atomic, hop);
+		break;
+
+	case _EMUX_OSS_INITIAL_VOLUME:
+		if (p2) {
+			port->volume_atten = (short)p1;
+			snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME);
+		}
+		break;
+
+	case _EMUX_OSS_CHN_PRESSURE:
+		if (chan) {
+			chan->midi_pressure = p1;
+			snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_FMMOD|SNDRV_EMUX_UPDATE_FM2FRQ2);
+		}
+		break;
+
+	case _EMUX_OSS_CHANNEL_MODE:
+		reset_port_mode(port, p1);
+		snd_emux_reset_port(port);
+		break;
+
+	case _EMUX_OSS_DRUM_CHANNELS:
+		port->drum_flags = *(unsigned int*)&event[4];
+		for (i = 0; i < port->chset.max_channels; i++) {
+			chan = &port->chset.channels[i];
+			chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0;
+		}
+		break;
+
+	case _EMUX_OSS_MISC_MODE:
+		if (p1 < EMUX_MD_END)
+			port->ctrls[p1] = p2;
+		break;
+	case _EMUX_OSS_DEBUG_MODE:
+		break;
+
+	default:
+		if (emu->ops.oss_ioctl)
+			emu->ops.oss_ioctl(emu, cmd, p1, p2);
+		break;
+	}
+}
+
+/*
+ * GUS specific h/w controls
+ */
+
+#include <linux/ultrasound.h>
+
+static void
+gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd,
+		unsigned char *event, int atomic, int hop)
+{
+	int voice;
+	unsigned short p1;
+	short p2;
+	int plong;
+	snd_midi_channel_t *chan;
+
+	if (port->port_mode != SNDRV_EMUX_PORT_MODE_OSS_SYNTH)
+		return;
+	if (cmd == _GUS_NUMVOICES)
+		return;
+	voice = event[3];
+	if (voice < 0 || voice >= port->chset.max_channels)
+		return;
+
+	chan = &port->chset.channels[voice];
+
+	p1 = *(unsigned short *) &event[4];
+	p2 = *(short *) &event[6];
+	plong = *(int*) &event[4];
+
+	switch (cmd) {
+	case _GUS_VOICESAMPLE:
+		chan->midi_program = p1;
+		return;
+
+	case _GUS_VOICEBALA:
+		/* 0 to 15 --> 0 to 127 */
+		chan->control[MIDI_CTL_MSB_PAN] = (int)p1 << 3;
+		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN);
+		return;
+
+	case _GUS_VOICEVOL:
+	case _GUS_VOICEVOL2:
+		/* not supported yet */
+		return;
+
+	case _GUS_RAMPRANGE:
+	case _GUS_RAMPRATE:
+	case _GUS_RAMPMODE:
+	case _GUS_RAMPON:
+	case _GUS_RAMPOFF:
+		/* volume ramping not supported */
+		return;
+
+	case _GUS_VOLUME_SCALE:
+		return;
+
+	case _GUS_VOICE_POS:
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+		snd_emux_send_effect(port, chan, EMUX_FX_SAMPLE_START,
+				     (short)(plong & 0x7fff),
+				     EMUX_FX_FLAG_SET);
+		snd_emux_send_effect(port, chan, EMUX_FX_COARSE_SAMPLE_START,
+				     (plong >> 15) & 0xffff,
+				     EMUX_FX_FLAG_SET);
+#endif
+		return;
+	}
+}
+
+
+/*
+ * send an event to midi emulation
+ */
+static void
+fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop)
+{
+	snd_seq_event_t ev;
+	memset(&ev, 0, sizeof(ev));
+	ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
+	ev.data.control.channel = ch;
+	ev.data.control.param = param;
+	ev.data.control.value = val;
+	snd_emux_event_input(&ev, 0, port, atomic, hop);
+}
+
+#endif /* CONFIG_SND_SEQUENCER_OSS */
diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c
new file mode 100644
index 0000000..0f155d6
--- /dev/null
+++ b/sound/synth/emux/emux_proc.c
@@ -0,0 +1,138 @@
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Proc interface for Emu8k/Emu10k1 WaveTable synth
+ *
+ *   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 <sound/info.h>
+#include "emux_voice.h"
+
+#ifdef CONFIG_PROC_FS
+
+static void
+snd_emux_proc_info_read(snd_info_entry_t *entry, 
+			snd_info_buffer_t *buf)
+{
+	snd_emux_t *emu;
+	int i;
+
+	emu = entry->private_data;
+	down(&emu->register_mutex);
+	if (emu->name)
+		snd_iprintf(buf, "Device: %s\n", emu->name);
+	snd_iprintf(buf, "Ports: %d\n", emu->num_ports);
+	snd_iprintf(buf, "Addresses:");
+	for (i = 0; i < emu->num_ports; i++)
+		snd_iprintf(buf, " %d:%d", emu->client, emu->ports[i]);
+	snd_iprintf(buf, "\n");
+	snd_iprintf(buf, "Use Counter: %d\n", emu->used);
+	snd_iprintf(buf, "Max Voices: %d\n", emu->max_voices);
+	snd_iprintf(buf, "Allocated Voices: %d\n", emu->num_voices);
+	if (emu->memhdr) {
+		snd_iprintf(buf, "Memory Size: %d\n", emu->memhdr->size);
+		snd_iprintf(buf, "Memory Available: %d\n", snd_util_mem_avail(emu->memhdr));
+		snd_iprintf(buf, "Allocated Blocks: %d\n", emu->memhdr->nblocks);
+	} else {
+		snd_iprintf(buf, "Memory Size: 0\n");
+	}
+	if (emu->sflist) {
+		down(&emu->sflist->presets_mutex);
+		snd_iprintf(buf, "SoundFonts: %d\n", emu->sflist->fonts_size);
+		snd_iprintf(buf, "Instruments: %d\n", emu->sflist->zone_counter);
+		snd_iprintf(buf, "Samples: %d\n", emu->sflist->sample_counter);
+		snd_iprintf(buf, "Locked Instruments: %d\n", emu->sflist->zone_locked);
+		snd_iprintf(buf, "Locked Samples: %d\n", emu->sflist->sample_locked);
+		up(&emu->sflist->presets_mutex);
+	}
+#if 0  /* debug */
+	if (emu->voices[0].state != SNDRV_EMUX_ST_OFF && emu->voices[0].ch >= 0) {
+		snd_emux_voice_t *vp = &emu->voices[0];
+		snd_iprintf(buf, "voice 0: on\n");
+		snd_iprintf(buf, "mod delay=%x, atkhld=%x, dcysus=%x, rel=%x\n",
+			    vp->reg.parm.moddelay,
+			    vp->reg.parm.modatkhld,
+			    vp->reg.parm.moddcysus,
+			    vp->reg.parm.modrelease);
+		snd_iprintf(buf, "vol delay=%x, atkhld=%x, dcysus=%x, rel=%x\n",
+			    vp->reg.parm.voldelay,
+			    vp->reg.parm.volatkhld,
+			    vp->reg.parm.voldcysus,
+			    vp->reg.parm.volrelease);
+		snd_iprintf(buf, "lfo1 delay=%x, lfo2 delay=%x, pefe=%x\n",
+			    vp->reg.parm.lfo1delay,
+			    vp->reg.parm.lfo2delay,
+			    vp->reg.parm.pefe);
+		snd_iprintf(buf, "fmmod=%x, tremfrq=%x, fm2frq2=%x\n",
+			    vp->reg.parm.fmmod,
+			    vp->reg.parm.tremfrq,
+			    vp->reg.parm.fm2frq2);
+		snd_iprintf(buf, "cutoff=%x, filterQ=%x, chorus=%x, reverb=%x\n",
+			    vp->reg.parm.cutoff,
+			    vp->reg.parm.filterQ,
+			    vp->reg.parm.chorus,
+			    vp->reg.parm.reverb);
+		snd_iprintf(buf, "avol=%x, acutoff=%x, apitch=%x\n",
+			    vp->avol, vp->acutoff, vp->apitch);
+		snd_iprintf(buf, "apan=%x, aaux=%x, ptarget=%x, vtarget=%x, ftarget=%x\n",
+			    vp->apan, vp->aaux,
+			    vp->ptarget,
+			    vp->vtarget,
+			    vp->ftarget);
+		snd_iprintf(buf, "start=%x, end=%x, loopstart=%x, loopend=%x\n",
+			    vp->reg.start, vp->reg.end, vp->reg.loopstart, vp->reg.loopend);
+		snd_iprintf(buf, "sample_mode=%x, rate=%x\n", vp->reg.sample_mode, vp->reg.rate_offset);
+	}
+#endif
+	up(&emu->register_mutex);
+}
+
+
+void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device)
+{
+	snd_info_entry_t *entry;
+	char name[64];
+
+	sprintf(name, "wavetableD%d", device);
+	entry = snd_info_create_card_entry(card, name, card->proc_root);
+	if (entry == NULL)
+		return;
+
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->private_data = emu;
+	entry->c.text.read_size = 1024;
+	entry->c.text.read = snd_emux_proc_info_read;
+	if (snd_info_register(entry) < 0)
+		snd_info_free_entry(entry);
+	else
+		emu->proc = entry;
+}
+
+void snd_emux_proc_free(snd_emux_t *emu)
+{
+	if (emu->proc) {
+		snd_info_unregister(emu->proc);
+		emu->proc = NULL;
+	}
+}
+
+#endif /* CONFIG_PROC_FS */
diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c
new file mode 100644
index 0000000..e41b28d
--- /dev/null
+++ b/sound/synth/emux/emux_seq.c
@@ -0,0 +1,428 @@
+/*
+ *  Midi Sequencer interface routines.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  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 <linux/slab.h>
+
+
+/* Prototypes for static functions */
+static void free_port(void *private);
+static void snd_emux_init_port(snd_emux_port_t *p);
+static int snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info);
+static int snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info);
+static int get_client(snd_card_t *card, int index, char *name);
+
+/*
+ * MIDI emulation operators
+ */
+static snd_midi_op_t emux_ops = {
+	snd_emux_note_on,
+	snd_emux_note_off,
+	snd_emux_key_press,
+	snd_emux_terminate_note,
+	snd_emux_control,
+	snd_emux_nrpn,
+	snd_emux_sysex,
+};
+
+
+/*
+ * number of MIDI channels
+ */
+#define MIDI_CHANNELS		16
+
+/*
+ * type flags for MIDI sequencer port
+ */
+#define DEFAULT_MIDI_TYPE	(SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |\
+				 SNDRV_SEQ_PORT_TYPE_MIDI_GM |\
+				 SNDRV_SEQ_PORT_TYPE_MIDI_GS |\
+				 SNDRV_SEQ_PORT_TYPE_MIDI_XG |\
+				 SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE)
+
+/*
+ * Initialise the EMUX Synth by creating a client and registering
+ * a series of ports.
+ * Each of the ports will contain the 16 midi channels.  Applications
+ * can connect to these ports to play midi data.
+ */
+int
+snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index)
+{
+	int  i;
+	snd_seq_port_callback_t pinfo;
+	char tmpname[64];
+
+	sprintf(tmpname, "%s WaveTable", emu->name);
+	emu->client = get_client(card, index, tmpname);
+	if (emu->client < 0) {
+		snd_printk("can't create client\n");
+		return -ENODEV;
+	}
+
+	if (emu->num_ports < 0) {
+		snd_printk("seqports must be greater than zero\n");
+		emu->num_ports = 1;
+	} else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) {
+		snd_printk("too many ports."
+			   "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS);
+		emu->num_ports = SNDRV_EMUX_MAX_PORTS;
+	}
+
+	memset(&pinfo, 0, sizeof(pinfo));
+	pinfo.owner = THIS_MODULE;
+	pinfo.use = snd_emux_use;
+	pinfo.unuse = snd_emux_unuse;
+	pinfo.event_input = snd_emux_event_input;
+
+	for (i = 0; i < emu->num_ports; i++) {
+		snd_emux_port_t *p;
+
+		sprintf(tmpname, "%s Port %d", emu->name, i);
+		p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS,
+					 0, &pinfo);
+		if (p == NULL) {
+			snd_printk("can't create port\n");
+			return -ENOMEM;
+		}
+
+		p->port_mode =  SNDRV_EMUX_PORT_MODE_MIDI;
+		snd_emux_init_port(p);
+		emu->ports[i] = p->chset.port;
+		emu->portptrs[i] = p;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Detach from the ports that were set up for this synthesizer and
+ * destroy the kernel client.
+ */
+void
+snd_emux_detach_seq(snd_emux_t *emu)
+{
+	if (emu->voices)
+		snd_emux_terminate_all(emu);
+		
+	down(&emu->register_mutex);
+	if (emu->client >= 0) {
+		snd_seq_delete_kernel_client(emu->client);
+		emu->client = -1;
+	}
+	up(&emu->register_mutex);
+}
+
+
+/*
+ * create a sequencer port and channel_set
+ */
+
+snd_emux_port_t *
+snd_emux_create_port(snd_emux_t *emu, char *name,
+			int max_channels, int oss_port,
+			snd_seq_port_callback_t *callback)
+{
+	snd_emux_port_t *p;
+	int i, type, cap;
+
+	/* Allocate structures for this channel */
+	if ((p = kcalloc(1, sizeof(*p), GFP_KERNEL)) == NULL) {
+		snd_printk("no memory\n");
+		return NULL;
+	}
+	p->chset.channels = kcalloc(max_channels, sizeof(snd_midi_channel_t), GFP_KERNEL);
+	if (p->chset.channels == NULL) {
+		snd_printk("no memory\n");
+		kfree(p);
+		return NULL;
+	}
+	for (i = 0; i < max_channels; i++)
+		p->chset.channels[i].number = i;
+	p->chset.private_data = p;
+	p->chset.max_channels = max_channels;
+	p->emu = emu;
+	p->chset.client = emu->client;
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	snd_emux_create_effect(p);
+#endif
+	callback->private_free = free_port;
+	callback->private_data = p;
+
+	cap = SNDRV_SEQ_PORT_CAP_WRITE;
+	if (oss_port) {
+		type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
+	} else {
+		type = DEFAULT_MIDI_TYPE;
+		cap |= SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+	}
+
+	p->chset.port = snd_seq_event_port_attach(emu->client, callback,
+						  cap, type, max_channels,
+						  emu->max_voices, name);
+
+	return p;
+}
+
+
+/*
+ * release memory block for port
+ */
+static void
+free_port(void *private_data)
+{
+	snd_emux_port_t *p;
+
+	p = private_data;
+	if (p) {
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+		snd_emux_delete_effect(p);
+#endif
+		kfree(p->chset.channels);
+		kfree(p);
+	}
+}
+
+
+#define DEFAULT_DRUM_FLAGS	(1<<9)
+
+/*
+ * initialize the port specific parameters
+ */
+static void
+snd_emux_init_port(snd_emux_port_t *p)
+{
+	p->drum_flags = DEFAULT_DRUM_FLAGS;
+	p->volume_atten = 0;
+
+	snd_emux_reset_port(p);
+}
+
+
+/*
+ * reset port
+ */
+void
+snd_emux_reset_port(snd_emux_port_t *port)
+{
+	int i;
+
+	/* stop all sounds */
+	snd_emux_sounds_off_all(port);
+
+	snd_midi_channel_set_clear(&port->chset);
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	snd_emux_clear_effect(port);
+#endif
+
+	/* set port specific control parameters */
+	port->ctrls[EMUX_MD_DEF_BANK] = 0;
+	port->ctrls[EMUX_MD_DEF_DRUM] = 0;
+	port->ctrls[EMUX_MD_REALTIME_PAN] = 1;
+
+	for (i = 0; i < port->chset.max_channels; i++) {
+		snd_midi_channel_t *chan = port->chset.channels + i;
+		chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0;
+	}
+}
+
+
+/*
+ * input sequencer event
+ */
+int
+snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private_data,
+		     int atomic, int hop)
+{
+	snd_emux_port_t *port;
+
+	port = private_data;
+	snd_assert(port != NULL && ev != NULL, return -EINVAL);
+
+	snd_midi_process_event(&emux_ops, ev, &port->chset);
+
+	return 0;
+}
+
+
+/*
+ * increment usage count
+ */
+int
+snd_emux_inc_count(snd_emux_t *emu)
+{
+	emu->used++;
+	if (!try_module_get(emu->ops.owner))
+		goto __error;
+	if (!try_module_get(emu->card->module)) {
+		module_put(emu->ops.owner);
+	      __error:
+		emu->used--;
+		return 0;
+	}
+	return 1;
+}
+
+
+/*
+ * decrease usage count
+ */
+void
+snd_emux_dec_count(snd_emux_t *emu)
+{
+	module_put(emu->card->module);
+	emu->used--;
+	if (emu->used <= 0)
+		snd_emux_terminate_all(emu);
+	module_put(emu->ops.owner);
+}
+
+
+/*
+ * Routine that is called upon a first use of a particular port
+ */
+static int
+snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_emux_port_t *p;
+	snd_emux_t *emu;
+
+	p = private_data;
+	snd_assert(p != NULL, return -EINVAL);
+	emu = p->emu;
+	snd_assert(emu != NULL, return -EINVAL);
+
+	down(&emu->register_mutex);
+	snd_emux_init_port(p);
+	snd_emux_inc_count(emu);
+	up(&emu->register_mutex);
+	return 0;
+}
+
+/*
+ * Routine that is called upon the last unuse() of a particular port.
+ */
+static int
+snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info)
+{
+	snd_emux_port_t *p;
+	snd_emux_t *emu;
+
+	p = private_data;
+	snd_assert(p != NULL, return -EINVAL);
+	emu = p->emu;
+	snd_assert(emu != NULL, return -EINVAL);
+
+	down(&emu->register_mutex);
+	snd_emux_sounds_off_all(p);
+	snd_emux_dec_count(emu);
+	up(&emu->register_mutex);
+	return 0;
+}
+
+
+/*
+ * Create a sequencer client
+ */
+static int
+get_client(snd_card_t *card, int index, char *name)
+{
+	snd_seq_client_callback_t callbacks;
+	snd_seq_client_info_t cinfo;
+	int client;
+
+	memset(&callbacks, 0, sizeof(callbacks));
+	callbacks.private_data = NULL;
+	callbacks.allow_input = 1;
+	callbacks.allow_output = 1;
+
+	/* Find a free client, start from 1 as the MPU expects to use 0 */
+	client = snd_seq_create_kernel_client(card, index, &callbacks);
+	if (client < 0)
+		return client;
+
+	memset(&cinfo, 0, sizeof(cinfo));
+	cinfo.client = client;
+	cinfo.type = KERNEL_CLIENT;
+	strcpy(cinfo.name, name);
+	snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo);
+
+	return client;
+}
+
+
+/*
+ * attach virtual rawmidi devices
+ */
+int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card)
+{
+	int i;
+
+	emu->vmidi = NULL;
+	if (emu->midi_ports <= 0)
+		return 0;
+
+	emu->vmidi = kcalloc(emu->midi_ports, sizeof(snd_rawmidi_t*), GFP_KERNEL);
+	if (emu->vmidi == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < emu->midi_ports; i++) {
+		snd_rawmidi_t *rmidi;
+		snd_virmidi_dev_t *rdev;
+		if (snd_virmidi_new(card, emu->midi_devidx + i, &rmidi) < 0)
+			goto __error;
+		rdev = rmidi->private_data;
+		sprintf(rmidi->name, "%s Synth MIDI", emu->name);
+		rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH;
+		rdev->client = emu->client;
+		rdev->port = emu->ports[i];
+		if (snd_device_register(card, rmidi) < 0) {
+			snd_device_free(card, rmidi);
+			goto __error;
+		}
+		emu->vmidi[i] = rmidi;
+		//snd_printk("virmidi %d ok\n", i);
+	}
+	return 0;
+
+__error:
+	//snd_printk("error init..\n");
+	snd_emux_delete_virmidi(emu);
+	return -ENOMEM;
+}
+
+int snd_emux_delete_virmidi(snd_emux_t *emu)
+{
+	int i;
+
+	if (emu->vmidi == NULL)
+		return 0;
+
+	for (i = 0; i < emu->midi_ports; i++) {
+		if (emu->vmidi[i])
+			snd_device_free(emu->card, emu->vmidi[i]);
+	}
+	kfree(emu->vmidi);
+	emu->vmidi = NULL;
+	return 0;
+}
diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c
new file mode 100644
index 0000000..f13b038
--- /dev/null
+++ b/sound/synth/emux/emux_synth.c
@@ -0,0 +1,963 @@
+/*
+ *  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 <sound/asoundef.h>
+
+/*
+ * Prototypes
+ */
+
+/*
+ * Ensure a value is between two points
+ * macro evaluates its args more than once, so changed to upper-case.
+ */
+#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
+#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
+
+static int get_zone(snd_emux_t *emu, snd_emux_port_t *port, int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table);
+static int get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan);
+static void terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free);
+static void exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass);
+static void terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free);
+static void update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update);
+static void setup_voice(snd_emux_voice_t *vp);
+static int calc_pan(snd_emux_voice_t *vp);
+static int calc_volume(snd_emux_voice_t *vp);
+static int calc_pitch(snd_emux_voice_t *vp);
+
+
+/*
+ * Start a note.
+ */
+void
+snd_emux_note_on(void *p, int note, int vel, snd_midi_channel_t *chan)
+{
+	snd_emux_t *emu;
+	int i, key, nvoices;
+	snd_emux_voice_t *vp;
+	snd_sf_zone_t *table[SNDRV_EMUX_MAX_MULTI_VOICES];
+	unsigned long flags;
+	snd_emux_port_t *port;
+
+	port = p;
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.get_voice != NULL, return);
+	snd_assert(emu->ops.trigger != NULL, return);
+
+	key = note; /* remember the original note */
+	nvoices = get_zone(emu, port, &note, vel, chan, table);
+	if (! nvoices)
+		return;
+
+	/* exclusive note off */
+	for (i = 0; i < nvoices; i++) {
+		snd_sf_zone_t *zp = table[i];
+		if (zp && zp->v.exclusiveClass)
+			exclusive_note_off(emu, port, zp->v.exclusiveClass);
+	}
+
+#if 0 // seems not necessary
+	/* Turn off the same note on the same channel. */
+	terminate_note1(emu, key, chan, 0);
+#endif
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < nvoices; i++) {
+
+		/* set up each voice parameter */
+		/* at this stage, we don't trigger the voice yet. */
+
+		if (table[i] == NULL)
+			continue;
+
+		vp = emu->ops.get_voice(emu, port);
+		if (vp == NULL || vp->ch < 0)
+			continue;
+		snd_assert(vp->emu != NULL && vp->hw != NULL, return);
+		if (STATE_IS_PLAYING(vp->state))
+			emu->ops.terminate(vp);
+
+		vp->time = emu->use_time++;
+		vp->chan = chan;
+		vp->port = port;
+		vp->key = key;
+		vp->note = note;
+		vp->velocity = vel;
+		vp->zone = table[i];
+		if (vp->zone->sample)
+			vp->block = vp->zone->sample->block;
+		else
+			vp->block = NULL;
+
+		setup_voice(vp);
+
+		vp->state = SNDRV_EMUX_ST_STANDBY;
+		if (emu->ops.prepare) {
+			vp->state = SNDRV_EMUX_ST_OFF;
+			if (emu->ops.prepare(vp) >= 0)
+				vp->state = SNDRV_EMUX_ST_STANDBY;
+		}
+	}
+
+	/* start envelope now */
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (vp->state == SNDRV_EMUX_ST_STANDBY &&
+		    vp->chan == chan) {
+			emu->ops.trigger(vp);
+			vp->state = SNDRV_EMUX_ST_ON;
+			vp->ontime = jiffies; /* remember the trigger timing */
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) {
+		/* clear voice position for the next note on this channel */
+		snd_emux_effect_table_t *fx = chan->private;
+		if (fx) {
+			fx->flag[EMUX_FX_SAMPLE_START] = 0;
+			fx->flag[EMUX_FX_COARSE_SAMPLE_START] = 0;
+		}
+	}
+#endif
+}
+
+/*
+ * Release a note in response to a midi note off.
+ */
+void
+snd_emux_note_off(void *p, int note, int vel, snd_midi_channel_t *chan)
+{
+	int ch;
+	snd_emux_t *emu;
+	snd_emux_voice_t *vp;
+	unsigned long flags;
+	snd_emux_port_t *port;
+
+	port = p;
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.release != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (ch = 0; ch < emu->max_voices; ch++) {
+		vp = &emu->voices[ch];
+		if (STATE_IS_PLAYING(vp->state) &&
+		    vp->chan == chan && vp->key == note) {
+			vp->time = emu->use_time++;
+			vp->state = SNDRV_EMUX_ST_RELEASED;
+			if (vp->ontime == jiffies) {
+				/* if note-off is sent too shortly after
+				 * note-on, emuX engine cannot produce the sound
+				 * correctly.  so we'll release this note
+				 * a bit later via timer callback.
+				 */
+				vp->state = SNDRV_EMUX_ST_PENDING;
+				if (! emu->timer_active) {
+					emu->tlist.expires = jiffies + 1;
+					add_timer(&emu->tlist);
+					emu->timer_active = 1;
+				}
+			} else
+				/* ok now release the note */
+				emu->ops.release(vp);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ * timer callback
+ *
+ * release the pending note-offs
+ */
+void snd_emux_timer_callback(unsigned long data)
+{
+	snd_emux_t *emu = (snd_emux_t*) data;
+	snd_emux_voice_t *vp;
+	int ch, do_again = 0;
+
+	spin_lock(&emu->voice_lock);
+	for (ch = 0; ch < emu->max_voices; ch++) {
+		vp = &emu->voices[ch];
+		if (vp->state == SNDRV_EMUX_ST_PENDING) {
+			if (vp->ontime == jiffies)
+				do_again++; /* release this at the next interrupt */
+			else {
+				emu->ops.release(vp);
+				vp->state = SNDRV_EMUX_ST_RELEASED;
+			}
+		}
+	}
+	if (do_again) {
+		emu->tlist.expires = jiffies + 1;
+		add_timer(&emu->tlist);
+		emu->timer_active = 1;
+	} else
+		emu->timer_active = 0;
+	spin_unlock(&emu->voice_lock);
+}
+
+/*
+ * key pressure change
+ */
+void
+snd_emux_key_press(void *p, int note, int vel, snd_midi_channel_t *chan)
+{
+	int ch;
+	snd_emux_t *emu;
+	snd_emux_voice_t *vp;
+	unsigned long flags;
+	snd_emux_port_t *port;
+
+	port = p;
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.update != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (ch = 0; ch < emu->max_voices; ch++) {
+		vp = &emu->voices[ch];
+		if (vp->state == SNDRV_EMUX_ST_ON &&
+		    vp->chan == chan && vp->key == note) {
+			vp->velocity = vel;
+			update_voice(emu, vp, SNDRV_EMUX_UPDATE_VOLUME);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Modulate the voices which belong to the channel
+ */
+void
+snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update)
+{
+	snd_emux_t *emu;
+	snd_emux_voice_t *vp;
+	int i;
+	unsigned long flags;
+
+	if (! update)
+		return;
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.update != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (vp->chan == chan)
+			update_voice(emu, vp, update);
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ * Modulate all the voices which belong to the port.
+ */
+void
+snd_emux_update_port(snd_emux_port_t *port, int update)
+{
+	snd_emux_t *emu; 
+	snd_emux_voice_t *vp;
+	int i;
+	unsigned long flags;
+
+	if (! update)
+		return;
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.update != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (vp->port == port)
+			update_voice(emu, vp, update);
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Deal with a controler type event.  This includes all types of
+ * control events, not just the midi controllers
+ */
+void
+snd_emux_control(void *p, int type, snd_midi_channel_t *chan)
+{
+	snd_emux_port_t *port;
+
+	port = p;
+	snd_assert(port != NULL && chan != NULL, return);
+
+	switch (type) {
+	case MIDI_CTL_MSB_MAIN_VOLUME:
+	case MIDI_CTL_MSB_EXPRESSION:
+		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_VOLUME);
+		break;
+		
+	case MIDI_CTL_MSB_PAN:
+		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN);
+		break;
+
+	case MIDI_CTL_SOFT_PEDAL:
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+		/* FIXME: this is an emulation */
+		snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, -160,
+				     EMUX_FX_FLAG_ADD);
+#endif
+		break;
+
+	case MIDI_CTL_PITCHBEND:
+		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PITCH);
+		break;
+
+	case MIDI_CTL_MSB_MODWHEEL:
+	case MIDI_CTL_CHAN_PRESSURE:
+		snd_emux_update_channel(port, chan,
+					SNDRV_EMUX_UPDATE_FMMOD |
+					SNDRV_EMUX_UPDATE_FM2FRQ2);
+		break;
+
+	}
+
+	if (port->chset.midi_mode == SNDRV_MIDI_MODE_XG) {
+		snd_emux_xg_control(port, chan, type);
+	}
+}
+
+
+/*
+ * terminate note - if free flag is true, free the terminated voice
+ */
+static void
+terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free)
+{
+	int  i;
+	snd_emux_voice_t *vp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state) && vp->chan == chan &&
+		    vp->key == note)
+			terminate_voice(emu, vp, free);
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * terminate note - exported for midi emulation
+ */
+void
+snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan)
+{
+	snd_emux_t *emu;
+	snd_emux_port_t *port;
+
+	port = p;
+	snd_assert(port != NULL && chan != NULL, return);
+
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.terminate != NULL, return);
+
+	terminate_note1(emu, note, chan, 1);
+}
+
+
+/*
+ * Terminate all the notes
+ */
+void
+snd_emux_terminate_all(snd_emux_t *emu)
+{
+	int i;
+	snd_emux_voice_t *vp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state))
+			terminate_voice(emu, vp, 0);
+		if (vp->state == SNDRV_EMUX_ST_OFF) {
+			if (emu->ops.free_voice)
+				emu->ops.free_voice(vp);
+			if (emu->ops.reset)
+				emu->ops.reset(emu, i);
+		}
+		vp->time = 0;
+	}
+	/* initialize allocation time */
+	emu->use_time = 0;
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Terminate all voices associated with the given port
+ */
+void
+snd_emux_sounds_off_all(snd_emux_port_t *port)
+{
+	int i;
+	snd_emux_t *emu;
+	snd_emux_voice_t *vp;
+	unsigned long flags;
+
+	snd_assert(port != NULL, return);
+	emu = port->emu;
+	snd_assert(emu != NULL, return);
+	snd_assert(emu->ops.terminate != NULL, return);
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state) &&
+		    vp->port == port)
+			terminate_voice(emu, vp, 0);
+		if (vp->state == SNDRV_EMUX_ST_OFF) {
+			if (emu->ops.free_voice)
+				emu->ops.free_voice(vp);
+			if (emu->ops.reset)
+				emu->ops.reset(emu, i);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+
+/*
+ * Terminate all voices that have the same exclusive class.  This
+ * is mainly for drums.
+ */
+static void
+exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass)
+{
+	snd_emux_voice_t *vp;
+	int  i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		if (STATE_IS_PLAYING(vp->state) && vp->port == port &&
+		    vp->reg.exclusiveClass == exclass) {
+			terminate_voice(emu, vp, 0);
+		}
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ * terminate a voice
+ * if free flag is true, call free_voice after termination
+ */
+static void
+terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free)
+{
+	emu->ops.terminate(vp);
+	vp->time = emu->use_time++;
+	vp->chan = NULL;
+	vp->port = NULL;
+	vp->zone = NULL;
+	vp->block = NULL;
+	vp->state = SNDRV_EMUX_ST_OFF;
+	if (free && emu->ops.free_voice)
+		emu->ops.free_voice(vp);
+}
+
+
+/*
+ * Modulate the voice
+ */
+static void
+update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update)
+{
+	if (!STATE_IS_PLAYING(vp->state))
+		return;
+
+	if (vp->chan == NULL || vp->port == NULL)
+		return;
+	if (update & SNDRV_EMUX_UPDATE_VOLUME)
+		calc_volume(vp);
+	if (update & SNDRV_EMUX_UPDATE_PITCH)
+		calc_pitch(vp);
+	if (update & SNDRV_EMUX_UPDATE_PAN) {
+		if (! calc_pan(vp) && (update == SNDRV_EMUX_UPDATE_PAN))
+			return;
+	}
+	emu->ops.update(vp, update);
+}
+
+
+#if 0 // not used
+/* table for volume target calculation */
+static unsigned short voltarget[16] = { 
+	0xEAC0, 0xE0C8, 0xD740, 0xCE20, 0xC560, 0xBD08, 0xB500, 0xAD58,
+	0xA5F8, 0x9EF0, 0x9830, 0x91C0, 0x8B90, 0x85A8, 0x8000, 0x7A90
+};
+#endif
+
+#define LO_BYTE(v)	((v) & 0xff)
+#define HI_BYTE(v)	(((v) >> 8) & 0xff)
+
+/*
+ * Sets up the voice structure by calculating some values that
+ * will be needed later.
+ */
+static void
+setup_voice(snd_emux_voice_t *vp)
+{
+	soundfont_voice_parm_t *parm;
+	int pitch;
+
+	/* copy the original register values */
+	vp->reg = vp->zone->v;
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	snd_emux_setup_effect(vp);
+#endif
+
+	/* reset status */
+	vp->apan = -1;
+	vp->avol = -1;
+	vp->apitch = -1;
+
+	calc_volume(vp);
+	calc_pitch(vp);
+	calc_pan(vp);
+
+	parm = &vp->reg.parm;
+
+	/* compute filter target and correct modulation parameters */
+	if (LO_BYTE(parm->modatkhld) >= 0x80 && parm->moddelay >= 0x8000) {
+		parm->moddelay = 0xbfff;
+		pitch = (HI_BYTE(parm->pefe) << 4) + vp->apitch;
+		if (pitch > 0xffff)
+			pitch = 0xffff;
+		/* calculate filter target */
+		vp->ftarget = parm->cutoff + LO_BYTE(parm->pefe);
+		LIMITVALUE(vp->ftarget, 0, 255);
+		vp->ftarget <<= 8;
+	} else {
+		vp->ftarget = parm->cutoff;
+		vp->ftarget <<= 8;
+		pitch = vp->apitch;
+	}
+
+	/* compute pitch target */
+	if (pitch != 0xffff) {
+		vp->ptarget = 1 << (pitch >> 12);
+		if (pitch & 0x800) vp->ptarget += (vp->ptarget*0x102e)/0x2710;
+		if (pitch & 0x400) vp->ptarget += (vp->ptarget*0x764)/0x2710;
+		if (pitch & 0x200) vp->ptarget += (vp->ptarget*0x389)/0x2710;
+		vp->ptarget += (vp->ptarget >> 1);
+		if (vp->ptarget > 0xffff) vp->ptarget = 0xffff;
+	} else
+		vp->ptarget = 0xffff;
+
+	if (LO_BYTE(parm->modatkhld) >= 0x80) {
+		parm->modatkhld &= ~0xff;
+		parm->modatkhld |= 0x7f;
+	}
+
+	/* compute volume target and correct volume parameters */
+	vp->vtarget = 0;
+#if 0 /* FIXME: this leads to some clicks.. */
+	if (LO_BYTE(parm->volatkhld) >= 0x80 && parm->voldelay >= 0x8000) {
+		parm->voldelay = 0xbfff;
+		vp->vtarget = voltarget[vp->avol % 0x10] >> (vp->avol >> 4);
+	}
+#endif
+
+	if (LO_BYTE(parm->volatkhld) >= 0x80) {
+		parm->volatkhld &= ~0xff;
+		parm->volatkhld |= 0x7f;
+	}
+}
+
+/*
+ * calculate pitch parameter
+ */
+static unsigned char pan_volumes[256] = {
+0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x14,0x17,0x1a,0x1d,0x20,0x22,0x25,0x28,0x2a,
+0x2d,0x30,0x32,0x35,0x37,0x3a,0x3c,0x3f,0x41,0x44,0x46,0x49,0x4b,0x4d,0x50,0x52,
+0x54,0x57,0x59,0x5b,0x5d,0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6f,0x71,0x73,0x75,
+0x77,0x79,0x7b,0x7c,0x7e,0x80,0x82,0x84,0x86,0x88,0x89,0x8b,0x8d,0x8f,0x90,0x92,
+0x94,0x96,0x97,0x99,0x9a,0x9c,0x9e,0x9f,0xa1,0xa2,0xa4,0xa5,0xa7,0xa8,0xaa,0xab,
+0xad,0xae,0xaf,0xb1,0xb2,0xb3,0xb5,0xb6,0xb7,0xb9,0xba,0xbb,0xbc,0xbe,0xbf,0xc0,
+0xc1,0xc2,0xc3,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,0xd0,0xd1,
+0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdc,0xdd,0xde,0xdf,
+0xdf,0xe0,0xe1,0xe2,0xe2,0xe3,0xe4,0xe4,0xe5,0xe6,0xe6,0xe7,0xe8,0xe8,0xe9,0xe9,
+0xea,0xeb,0xeb,0xec,0xec,0xed,0xed,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf1,0xf1,0xf1,
+0xf2,0xf2,0xf3,0xf3,0xf3,0xf4,0xf4,0xf5,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,0xf7,
+0xf7,0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfb,0xfb,0xfb,
+0xfb,0xfb,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,
+0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+static int
+calc_pan(snd_emux_voice_t *vp)
+{
+	snd_midi_channel_t *chan = vp->chan;
+	int pan;
+
+	/* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */
+	if (vp->reg.fixpan > 0)	/* 0-127 */
+		pan = 255 - (int)vp->reg.fixpan * 2;
+	else {
+		pan = chan->control[MIDI_CTL_MSB_PAN] - 64;
+		if (vp->reg.pan >= 0) /* 0-127 */
+			pan += vp->reg.pan - 64;
+		pan = 127 - (int)pan * 2;
+	}
+	LIMITVALUE(pan, 0, 255);
+
+	if (vp->emu->linear_panning) {
+		/* assuming linear volume */
+		if (pan != vp->apan) {
+			vp->apan = pan;
+			if (pan == 0)
+				vp->aaux = 0xff;
+			else
+				vp->aaux = (-pan) & 0xff;
+			return 1;
+		} else
+			return 0;
+	} else {
+		/* using volume table */
+		if (vp->apan != (int)pan_volumes[pan]) {
+			vp->apan = pan_volumes[pan];
+			vp->aaux = pan_volumes[255 - pan];
+			return 1;
+		}
+		return 0;
+	}
+}
+
+
+/*
+ * calculate volume attenuation
+ *
+ * Voice volume is controlled by volume attenuation parameter.
+ * So volume becomes maximum when avol is 0 (no attenuation), and
+ * minimum when 255 (-96dB or silence).
+ */
+
+/* tables for volume->attenuation calculation */
+static unsigned char voltab1[128] = {
+   0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+   0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22,
+   0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a,
+   0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14,
+   0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10,
+   0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d,
+   0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b,
+   0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09,
+   0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06,
+   0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04,
+   0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
+   0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char voltab2[128] = {
+   0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a,
+   0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21,
+   0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a,
+   0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15,
+   0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10,
+   0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d,
+   0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a,
+   0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08,
+   0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
+   0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+   0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
+   0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+   0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char expressiontab[128] = {
+   0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42,
+   0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30,
+   0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25,
+   0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e,
+   0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18,
+   0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13,
+   0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f,
+   0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c,
+   0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09,
+   0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
+   0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03,
+   0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
+   0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * Magic to calculate the volume (actually attenuation) from all the
+ * voice and channels parameters.
+ */
+static int
+calc_volume(snd_emux_voice_t *vp)
+{
+	int vol;
+	int main_vol, expression_vol, master_vol;
+	snd_midi_channel_t *chan = vp->chan;
+	snd_emux_port_t *port = vp->port;
+
+	expression_vol = chan->control[MIDI_CTL_MSB_EXPRESSION];
+	LIMITMAX(vp->velocity, 127);
+	LIMITVALUE(expression_vol, 0, 127);
+	if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) {
+		/* 0 - 127 */
+		main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME];
+		vol = (vp->velocity * main_vol * expression_vol) / (127*127);
+		vol = vol * vp->reg.amplitude / 127;
+
+		LIMITVALUE(vol, 0, 127);
+
+		/* calc to attenuation */
+		vol = snd_sf_vol_table[vol];
+
+	} else {
+		main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME] * vp->reg.amplitude / 127;
+		LIMITVALUE(main_vol, 0, 127);
+
+		vol = voltab1[main_vol] + voltab2[vp->velocity];
+		vol = (vol * 8) / 3;
+		vol += vp->reg.attenuation;
+		vol += ((0x100 - vol) * expressiontab[expression_vol])/128;
+	}
+
+	master_vol = port->chset.gs_master_volume;
+	LIMITVALUE(master_vol, 0, 127);
+	vol += snd_sf_vol_table[master_vol];
+	vol += port->volume_atten;
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	if (chan->private) {
+		snd_emux_effect_table_t *fx = chan->private;
+		vol += fx->val[EMUX_FX_ATTEN];
+	}
+#endif
+
+	LIMITVALUE(vol, 0, 255);
+	if (vp->avol == vol)
+		return 0; /* value unchanged */
+
+	vp->avol = vol;
+	if (!SF_IS_DRUM_BANK(get_bank(port, chan))
+	    && LO_BYTE(vp->reg.parm.volatkhld) < 0x7d) {
+		int atten;
+		if (vp->velocity < 70)
+			atten = 70;
+		else
+			atten = vp->velocity;
+		vp->acutoff = (atten * vp->reg.parm.cutoff + 0xa0) >> 7;
+	} else {
+		vp->acutoff = vp->reg.parm.cutoff;
+	}
+
+	return 1; /* value changed */
+}
+
+/*
+ * calculate pitch offset
+ *
+ * 0xE000 is no pitch offset at 44100Hz sample.
+ * Every 4096 is one octave.
+ */
+
+static int
+calc_pitch(snd_emux_voice_t *vp)
+{
+	snd_midi_channel_t *chan = vp->chan;
+	int offset;
+
+	/* calculate offset */
+	if (vp->reg.fixkey >= 0) {
+		offset = (vp->reg.fixkey - vp->reg.root) * 4096 / 12;
+	} else {
+		offset = (vp->note - vp->reg.root) * 4096 / 12;
+	}
+	offset = (offset * vp->reg.scaleTuning) / 100;
+	offset += vp->reg.tune * 4096 / 1200;
+	if (chan->midi_pitchbend != 0) {
+		/* (128 * 8192: 1 semitone) ==> (4096: 12 semitones) */
+		offset += chan->midi_pitchbend * chan->gm_rpn_pitch_bend_range / 3072;
+	}
+
+	/* tuning via RPN:
+	 *   coarse = -8192 to 8192 (100 cent per 128)
+	 *   fine = -8192 to 8192 (max=100cent)
+	 */
+	/* 4096 = 1200 cents in emu8000 parameter */
+	offset += chan->gm_rpn_coarse_tuning * 4096 / (12 * 128);
+	offset += chan->gm_rpn_fine_tuning / 24;
+
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+	/* add initial pitch correction */
+	if (chan->private) {
+		snd_emux_effect_table_t *fx = chan->private;
+		if (fx->flag[EMUX_FX_INIT_PITCH])
+			offset += fx->val[EMUX_FX_INIT_PITCH];
+	}
+#endif
+
+	/* 0xe000: root pitch */
+	offset += 0xe000 + vp->reg.rate_offset;
+	offset += vp->emu->pitch_shift;
+	LIMITVALUE(offset, 0, 0xffff);
+	if (offset == vp->apitch)
+		return 0; /* unchanged */
+	vp->apitch = offset;
+	return 1; /* value changed */
+}
+
+/*
+ * Get the bank number assigned to the channel
+ */
+static int
+get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan)
+{
+	int val;
+
+	switch (port->chset.midi_mode) {
+	case SNDRV_MIDI_MODE_XG:
+		val = chan->control[MIDI_CTL_MSB_BANK];
+		if (val == 127)
+			return 128; /* return drum bank */
+		return chan->control[MIDI_CTL_LSB_BANK];
+
+	case SNDRV_MIDI_MODE_GS:
+		if (chan->drum_channel)
+			return 128;
+		/* ignore LSB (bank map) */
+		return chan->control[MIDI_CTL_MSB_BANK];
+		
+	default:
+		if (chan->drum_channel)
+			return 128;
+		return chan->control[MIDI_CTL_MSB_BANK];
+	}
+}
+
+
+/* Look for the zones matching with the given note and velocity.
+ * The resultant zones are stored on table.
+ */
+static int
+get_zone(snd_emux_t *emu, snd_emux_port_t *port,
+	 int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table)
+{
+	int preset, bank, def_preset, def_bank;
+
+	bank = get_bank(port, chan);
+	preset = chan->midi_program;
+
+	if (SF_IS_DRUM_BANK(bank)) {
+		def_preset = port->ctrls[EMUX_MD_DEF_DRUM];
+		def_bank = bank;
+	} else {
+		def_preset = preset;
+		def_bank = port->ctrls[EMUX_MD_DEF_BANK];
+	}
+
+	return snd_soundfont_search_zone(emu->sflist, notep, vel, preset, bank,
+					 def_preset, def_bank,
+					 table, SNDRV_EMUX_MAX_MULTI_VOICES);
+}
+
+/*
+ */
+void
+snd_emux_init_voices(snd_emux_t *emu)
+{
+	snd_emux_voice_t *vp;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (i = 0; i < emu->max_voices; i++) {
+		vp = &emu->voices[i];
+		vp->ch = -1; /* not used */
+		vp->state = SNDRV_EMUX_ST_OFF;
+		vp->chan = NULL;
+		vp->port = NULL;
+		vp->time = 0;
+		vp->emu = emu;
+		vp->hw = emu->hw;
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ */
+void snd_emux_lock_voice(snd_emux_t *emu, int voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	if (emu->voices[voice].state == SNDRV_EMUX_ST_OFF)
+		emu->voices[voice].state = SNDRV_EMUX_ST_LOCKED;
+	else
+		snd_printk("invalid voice for lock %d (state = %x)\n",
+			   voice, emu->voices[voice].state);
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
+
+/*
+ */
+void snd_emux_unlock_voice(snd_emux_t *emu, int voice)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	if (emu->voices[voice].state == SNDRV_EMUX_ST_LOCKED)
+		emu->voices[voice].state = SNDRV_EMUX_ST_OFF;
+	else
+		snd_printk("invalid voice for unlock %d (state = %x)\n",
+			   voice, emu->voices[voice].state);
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+}
diff --git a/sound/synth/emux/emux_voice.h b/sound/synth/emux/emux_voice.h
new file mode 100644
index 0000000..67eb553
--- /dev/null
+++ b/sound/synth/emux/emux_voice.h
@@ -0,0 +1,88 @@
+#ifndef __EMUX_VOICE_H
+#define __EMUX_VOICE_H
+
+/*
+ * A structure to keep track of each hardware voice
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  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 <sound/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <sound/core.h>
+#include <sound/emux_synth.h>
+
+/* Prototypes for emux_seq.c */
+int snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index);
+void snd_emux_detach_seq(snd_emux_t *emu);
+snd_emux_port_t *snd_emux_create_port(snd_emux_t *emu, char *name, int max_channels, int type, snd_seq_port_callback_t *callback);
+void snd_emux_reset_port(snd_emux_port_t *port);
+int snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop);
+int snd_emux_inc_count(snd_emux_t *emu);
+void snd_emux_dec_count(snd_emux_t *emu);
+int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card);
+int snd_emux_delete_virmidi(snd_emux_t *emu);
+
+/* Prototypes for emux_synth.c */
+void snd_emux_init_voices(snd_emux_t *emu);
+
+void snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan);
+void snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan);
+void snd_emux_control(void *p, int type, struct snd_midi_channel *chan);
+
+void snd_emux_sounds_off_all(snd_emux_port_t *port);
+void snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update);
+void snd_emux_update_port(snd_emux_port_t *port, int update);
+
+void snd_emux_timer_callback(unsigned long data);
+
+/* emux_effect.c */
+#ifdef SNDRV_EMUX_USE_RAW_EFFECT
+void snd_emux_create_effect(snd_emux_port_t *p);
+void snd_emux_delete_effect(snd_emux_port_t *p);
+void snd_emux_clear_effect(snd_emux_port_t *p);
+void snd_emux_setup_effect(snd_emux_voice_t *vp);
+void snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val);
+void snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode);
+#endif
+
+/* emux_nrpn.c */
+void snd_emux_sysex(void *private_data, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset);
+int snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param);
+void snd_emux_nrpn(void *private_data, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset);
+
+/* emux_oss.c */
+void snd_emux_init_seq_oss(snd_emux_t *emu);
+void snd_emux_detach_seq_oss(snd_emux_t *emu);
+
+/* emux_proc.c */
+#ifdef CONFIG_PROC_FS
+void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device);
+void snd_emux_proc_free(snd_emux_t *emu);
+#endif
+
+#define STATE_IS_PLAYING(s) ((s) & SNDRV_EMUX_ST_ON)
+
+/* emux_hwdep.c */
+int snd_emux_init_hwdep(snd_emux_t *emu);
+void snd_emux_delete_hwdep(snd_emux_t *emu);
+
+#endif
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
new file mode 100644
index 0000000..901a7db
--- /dev/null
+++ b/sound/synth/emux/soundfont.c
@@ -0,0 +1,1462 @@
+/*
+ *  Soundfont generic routines.
+ *	It is intended that these should be used by any driver that is willing
+ *	to accept soundfont patches.
+ *
+ *  Copyright (C) 1999 Steve Ratcliffe
+ *  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
+ */
+/*
+ * Deal with reading in of a soundfont.  Code follows the OSS way
+ * of doing things so that the old sfxload utility can be used.
+ * Everything may change when there is an alsa way of doing things.
+ */
+#include <sound/driver.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soundfont.h>
+#include <sound/seq_oss_legacy.h>
+
+/* Prototypes for static functions */
+
+static int open_patch(snd_sf_list_t *sflist, const char __user *data, int count, int client);
+static snd_soundfont_t *newsf(snd_sf_list_t *sflist, int type, char *name);
+static int is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name);
+static int close_patch(snd_sf_list_t *sflist);
+static int probe_data(snd_sf_list_t *sflist, int sample_id);
+static void set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp);
+static snd_sf_zone_t *sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf);
+static void set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp);
+static snd_sf_sample_t *sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf);
+static void sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp);
+static int load_map(snd_sf_list_t *sflist, const void __user *data, int count);
+static int load_info(snd_sf_list_t *sflist, const void __user *data, long count);
+static int remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr);
+static void init_voice_info(soundfont_voice_info_t *avp);
+static void init_voice_parm(soundfont_voice_parm_t *pp);
+static snd_sf_sample_t *set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp);
+static snd_sf_sample_t *find_sample(snd_soundfont_t *sf, int sample_id);
+static int load_data(snd_sf_list_t *sflist, const void __user *data, long count);
+static void rebuild_presets(snd_sf_list_t *sflist);
+static void add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur);
+static void delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp);
+static snd_sf_zone_t *search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key);
+static int search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level);
+static int get_index(int bank, int instr, int key);
+static void snd_sf_init(snd_sf_list_t *sflist);
+static void snd_sf_clear(snd_sf_list_t *sflist);
+
+/*
+ * lock access to sflist
+ */
+static void
+lock_preset(snd_sf_list_t *sflist)
+{
+	unsigned long flags;
+	down(&sflist->presets_mutex);
+	spin_lock_irqsave(&sflist->lock, flags);
+	sflist->presets_locked = 1;
+	spin_unlock_irqrestore(&sflist->lock, flags);
+}
+
+
+/*
+ * remove lock
+ */
+static void
+unlock_preset(snd_sf_list_t *sflist)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&sflist->lock, flags);
+	sflist->presets_locked = 0;
+	spin_unlock_irqrestore(&sflist->lock, flags);
+	up(&sflist->presets_mutex);
+}
+
+
+/*
+ * close the patch if the patch was opened by this client.
+ */
+int
+snd_soundfont_close_check(snd_sf_list_t *sflist, int client)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->open_client == client)  {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return close_patch(sflist);
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+	return 0;
+}
+
+
+/*
+ * Deal with a soundfont patch.  Any driver could use these routines
+ * although it was designed for the AWE64.
+ *
+ * The sample_write and callargs pararameters allow a callback into
+ * the actual driver to write sample data to the board or whatever
+ * it wants to do with it.
+ */
+int
+snd_soundfont_load(snd_sf_list_t *sflist, const void __user *data, long count, int client)
+{
+	soundfont_patch_info_t patch;
+	unsigned long flags;
+	int  rc;
+
+	if (count < (long)sizeof(patch)) {
+		snd_printk("patch record too small %ld\n", count);
+		return -EINVAL;
+	}
+	if (copy_from_user(&patch, data, sizeof(patch)))
+		return -EFAULT;
+
+	count -= sizeof(patch);
+	data += sizeof(patch);
+
+	if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) {
+		snd_printk("'The wrong kind of patch' %x\n", patch.key);
+		return -EINVAL;
+	}
+	if (count < patch.len) {
+		snd_printk("Patch too short %ld, need %d\n", count, patch.len);
+		return -EINVAL;
+	}
+	if (patch.len < 0) {
+		snd_printk("poor length %d\n", patch.len);
+		return -EINVAL;
+	}
+
+	if (patch.type == SNDRV_SFNT_OPEN_PATCH) {
+		/* grab sflist to open */
+		lock_preset(sflist);
+		rc = open_patch(sflist, data, count, client);
+		unlock_preset(sflist);
+		return rc;
+	}
+
+	/* check if other client already opened patch */
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->open_client != client) {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return -EBUSY;
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+
+	lock_preset(sflist);
+	rc = -EINVAL;
+	switch (patch.type) {
+	case SNDRV_SFNT_LOAD_INFO:
+		rc = load_info(sflist, data, count);
+		break;
+	case SNDRV_SFNT_LOAD_DATA:
+		rc = load_data(sflist, data, count);
+		break;
+	case SNDRV_SFNT_CLOSE_PATCH:
+		rc = close_patch(sflist);
+		break;
+	case SNDRV_SFNT_REPLACE_DATA:
+		/*rc = replace_data(&patch, data, count);*/
+		break;
+	case SNDRV_SFNT_MAP_PRESET:
+		rc = load_map(sflist, data, count);
+		break;
+	case SNDRV_SFNT_PROBE_DATA:
+		rc = probe_data(sflist, patch.optarg);
+		break;
+	case SNDRV_SFNT_REMOVE_INFO:
+		/* patch must be opened */
+		if (sflist->currsf) {
+			snd_printk("soundfont: remove_info: patch not opened\n");
+			rc = -EINVAL;
+		} else {
+			int bank, instr;
+			bank = ((unsigned short)patch.optarg >> 8) & 0xff;
+			instr = (unsigned short)patch.optarg & 0xff;
+			if (! remove_info(sflist, sflist->currsf, bank, instr))
+				rc = -EINVAL;
+			else
+				rc = 0;
+		}
+		break;
+	}
+	unlock_preset(sflist);
+
+	return rc;
+}
+
+
+/* check if specified type is special font (GUS or preset-alias) */
+static inline int
+is_special_type(int type)
+{
+	type &= 0x0f;
+	return (type == SNDRV_SFNT_PAT_TYPE_GUS ||
+		type == SNDRV_SFNT_PAT_TYPE_MAP);
+}
+
+
+/* open patch; create sf list */
+static int
+open_patch(snd_sf_list_t *sflist, const char __user *data, int count, int client)
+{
+	soundfont_open_parm_t parm;
+	snd_soundfont_t *sf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->open_client >= 0 || sflist->currsf) {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return -EBUSY;
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+
+	if (copy_from_user(&parm, data, sizeof(parm)))
+		return -EFAULT;
+
+	if (is_special_type(parm.type)) {
+		parm.type |= SNDRV_SFNT_PAT_SHARED;
+		sf = newsf(sflist, parm.type, NULL);
+	} else 
+		sf = newsf(sflist, parm.type, parm.name);
+	if (sf == NULL) {
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&sflist->lock, flags);
+	sflist->open_client = client;
+	sflist->currsf = sf;
+	spin_unlock_irqrestore(&sflist->lock, flags);
+
+	return 0;
+}
+
+/*
+ * Allocate a new soundfont structure.
+ */
+static snd_soundfont_t *
+newsf(snd_sf_list_t *sflist, int type, char *name)
+{
+	snd_soundfont_t *sf;
+
+	/* check the shared fonts */
+	if (type & SNDRV_SFNT_PAT_SHARED) {
+		for (sf = sflist->fonts; sf; sf = sf->next) {
+			if (is_identical_font(sf, type, name)) {
+				return sf;
+			}
+		}
+	}
+
+	/* not found -- create a new one */
+	sf = kcalloc(1, sizeof(*sf), GFP_KERNEL);
+	if (sf == NULL)
+		return NULL;
+	sf->id = sflist->fonts_size;
+	sflist->fonts_size++;
+
+	/* prepend this record */
+	sf->next = sflist->fonts;
+	sflist->fonts = sf;
+
+	sf->type = type;
+	sf->zones = NULL;
+	sf->samples = NULL;
+	if (name)
+		memcpy(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN);
+
+	return sf;
+}
+
+/* check if the given name matches to the existing list */
+static int
+is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name)
+{
+	return ((sf->type & SNDRV_SFNT_PAT_SHARED) &&
+		(sf->type & 0x0f) == (type & 0x0f) &&
+		(name == NULL ||
+		 memcmp(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN) == 0));
+}
+
+/*
+ * Close the current patch.
+ */
+static int
+close_patch(snd_sf_list_t *sflist)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sflist->lock, flags);
+	sflist->currsf = NULL;
+	sflist->open_client = -1;
+	spin_unlock_irqrestore(&sflist->lock, flags);
+
+	rebuild_presets(sflist);
+
+	return 0;
+
+}
+
+/* probe sample in the current list -- nothing to be loaded */
+static int
+probe_data(snd_sf_list_t *sflist, int sample_id)
+{
+	/* patch must be opened */
+	if (sflist->currsf) {
+		/* search the specified sample by optarg */
+		if (find_sample(sflist->currsf, sample_id))
+			return 0;
+	}
+	return -EINVAL;
+}
+
+/*
+ * increment zone counter
+ */
+static void
+set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp)
+{
+	zp->counter = sflist->zone_counter++;
+	if (sf->type & SNDRV_SFNT_PAT_LOCKED)
+		sflist->zone_locked = sflist->zone_counter;
+}
+
+/*
+ * allocate a new zone record
+ */
+static snd_sf_zone_t *
+sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf)
+{
+	snd_sf_zone_t *zp;
+
+	if ((zp = kcalloc(1, sizeof(*zp), GFP_KERNEL)) == NULL)
+		return NULL;
+	zp->next = sf->zones;
+	sf->zones = zp;
+
+	init_voice_info(&zp->v);
+
+	set_zone_counter(sflist, sf, zp);
+	return zp;
+}
+
+
+/*
+ * increment sample couter
+ */
+static void
+set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp)
+{
+	sp->counter = sflist->sample_counter++;
+	if (sf->type & SNDRV_SFNT_PAT_LOCKED)
+		sflist->sample_locked = sflist->sample_counter;
+}
+
+/*
+ * allocate a new sample list record
+ */
+static snd_sf_sample_t *
+sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf)
+{
+	snd_sf_sample_t *sp;
+
+	if ((sp = kcalloc(1, sizeof(*sp), GFP_KERNEL)) == NULL)
+		return NULL;
+
+	sp->next = sf->samples;
+	sf->samples = sp;
+
+	set_sample_counter(sflist, sf, sp);
+	return sp;
+}
+
+/*
+ * delete sample list -- this is an exceptional job.
+ * only the last allocated sample can be deleted.
+ */
+static void
+sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp)
+{
+	/* only last sample is accepted */
+	if (sp == sf->samples) {
+		sf->samples = sp->next;
+		kfree(sp);
+	}
+}
+
+
+/* load voice map */
+static int
+load_map(snd_sf_list_t *sflist, const void __user *data, int count)
+{
+	snd_sf_zone_t *zp, *prevp;
+	snd_soundfont_t *sf;
+	soundfont_voice_map_t map;
+
+	/* get the link info */
+	if (count < (int)sizeof(map))
+		return -EINVAL;
+	if (copy_from_user(&map, data, sizeof(map)))
+		return -EFAULT;
+
+	if (map.map_instr < 0 || map.map_instr >= SF_MAX_INSTRUMENTS)
+		return -EINVAL;
+	
+	sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_MAP|SNDRV_SFNT_PAT_SHARED, NULL);
+	if (sf == NULL)
+		return -ENOMEM;
+
+	prevp = NULL;
+	for (zp = sf->zones; zp; prevp = zp, zp = zp->next) {
+		if (zp->mapped &&
+		    zp->instr == map.map_instr &&
+		    zp->bank == map.map_bank &&
+		    zp->v.low == map.map_key &&
+		    zp->v.start == map.src_instr &&
+		    zp->v.end == map.src_bank &&
+		    zp->v.fixkey == map.src_key) {
+			/* the same mapping is already present */
+			/* relink this record to the link head */
+			if (prevp) {
+				prevp->next = zp->next;
+				zp->next = sf->zones;
+				sf->zones = zp;
+			}
+			/* update the counter */
+			set_zone_counter(sflist, sf, zp);
+			return 0;
+		}
+	}
+
+	/* create a new zone */
+	if ((zp = sf_zone_new(sflist, sf)) == NULL)
+		return -ENOMEM;
+
+	zp->bank = map.map_bank;
+	zp->instr = map.map_instr;
+	zp->mapped = 1;
+	if (map.map_key >= 0) {
+		zp->v.low = map.map_key;
+		zp->v.high = map.map_key;
+	}
+	zp->v.start = map.src_instr;
+	zp->v.end = map.src_bank;
+	zp->v.fixkey = map.src_key;
+	zp->v.sf_id = sf->id;
+
+	add_preset(sflist, zp);
+
+	return 0;
+}
+
+
+/* remove the present instrument layers */
+static int
+remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr)
+{
+	snd_sf_zone_t *prev, *next, *p;
+	int removed = 0;
+
+	prev = NULL;
+	for (p = sf->zones; p; p = next) {
+		next = p->next;
+		if (! p->mapped &&
+		    p->bank == bank && p->instr == instr) {
+			/* remove this layer */
+			if (prev)
+				prev->next = next;
+			else
+				sf->zones = next;
+			removed++;
+			kfree(p);
+		} else
+			prev = p;
+	}
+	if (removed)
+		rebuild_presets(sflist);
+	return removed;
+}
+
+
+/*
+ * Read an info record from the user buffer and save it on the current
+ * open soundfont.
+ */
+static int
+load_info(snd_sf_list_t *sflist, const void __user *data, long count)
+{
+	snd_soundfont_t *sf;
+	snd_sf_zone_t *zone;
+	soundfont_voice_rec_hdr_t hdr;
+	int i;
+
+	/* patch must be opened */
+	if ((sf = sflist->currsf) == NULL)
+		return -EINVAL;
+
+	if (is_special_type(sf->type))
+		return -EINVAL;
+
+	if (count < (long)sizeof(hdr)) {
+		printk("Soundfont error: invalid patch zone length\n");
+		return -EINVAL;
+	}
+	if (copy_from_user((char*)&hdr, data, sizeof(hdr)))
+		return -EFAULT;
+	
+	data += sizeof(hdr);
+	count -= sizeof(hdr);
+
+	if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
+		printk("Soundfont error: Illegal voice number %d\n", hdr.nvoices);
+		return -EINVAL;
+	}
+
+	if (count < (long)sizeof(soundfont_voice_info_t)*hdr.nvoices) {
+		printk("Soundfont Error: patch length(%ld) is smaller than nvoices(%d)\n",
+		       count, hdr.nvoices);
+		return -EINVAL;
+	}
+
+	switch (hdr.write_mode) {
+	case SNDRV_SFNT_WR_EXCLUSIVE:
+		/* exclusive mode - if the instrument already exists,
+		   return error */
+		for (zone = sf->zones; zone; zone = zone->next) {
+			if (!zone->mapped &&
+			    zone->bank == hdr.bank &&
+			    zone->instr == hdr.instr)
+				return -EINVAL;
+		}
+		break;
+	case SNDRV_SFNT_WR_REPLACE:
+		/* replace mode - remove the instrument if it already exists */
+		remove_info(sflist, sf, hdr.bank, hdr.instr);
+		break;
+	}
+
+	for (i = 0; i < hdr.nvoices; i++) {
+		snd_sf_zone_t tmpzone;
+
+		/* copy awe_voice_info parameters */
+		if (copy_from_user(&tmpzone.v, data, sizeof(tmpzone.v))) {
+			return -EFAULT;
+		}
+
+		data += sizeof(tmpzone.v);
+		count -= sizeof(tmpzone.v);
+
+		tmpzone.bank = hdr.bank;
+		tmpzone.instr = hdr.instr;
+		tmpzone.mapped = 0;
+		tmpzone.v.sf_id = sf->id;
+		if (tmpzone.v.mode & SNDRV_SFNT_MODE_INIT_PARM)
+			init_voice_parm(&tmpzone.v.parm);
+
+		/* create a new zone */
+		if ((zone = sf_zone_new(sflist, sf)) == NULL) {
+			return -ENOMEM;
+		}
+
+		/* copy the temporary data */
+		zone->bank = tmpzone.bank;
+		zone->instr = tmpzone.instr;
+		zone->v = tmpzone.v;
+
+		/* look up the sample */
+		zone->sample = set_sample(sf, &zone->v);
+	}
+
+	return 0;
+}
+
+
+/* initialize voice_info record */
+static void
+init_voice_info(soundfont_voice_info_t *avp)
+{
+	memset(avp, 0, sizeof(*avp));
+
+	avp->root = 60;
+	avp->high = 127;
+	avp->velhigh = 127;
+	avp->fixkey = -1;
+	avp->fixvel = -1;
+	avp->fixpan = -1;
+	avp->pan = -1;
+	avp->amplitude = 127;
+	avp->scaleTuning = 100;
+
+	init_voice_parm(&avp->parm);
+}
+
+/* initialize voice_parm record:
+ * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0.
+ * Vibrato and Tremolo effects are zero.
+ * Cutoff is maximum.
+ * Chorus and Reverb effects are zero.
+ */
+static void
+init_voice_parm(soundfont_voice_parm_t *pp)
+{
+	memset(pp, 0, sizeof(*pp));
+
+	pp->moddelay = 0x8000;
+	pp->modatkhld = 0x7f7f;
+	pp->moddcysus = 0x7f7f;
+	pp->modrelease = 0x807f;
+
+	pp->voldelay = 0x8000;
+	pp->volatkhld = 0x7f7f;
+	pp->voldcysus = 0x7f7f;
+	pp->volrelease = 0x807f;
+
+	pp->lfo1delay = 0x8000;
+	pp->lfo2delay = 0x8000;
+
+	pp->cutoff = 0xff;
+}	
+
+/* search the specified sample */
+static snd_sf_sample_t *
+set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp)
+{
+	snd_sf_sample_t *sample;
+
+	sample = find_sample(sf, avp->sample);
+	if (sample == NULL)
+		return NULL;
+
+	/* add in the actual sample offsets:
+	 * The voice_info addresses define only the relative offset
+	 * from sample pointers.  Here we calculate the actual DRAM
+	 * offset from sample pointers.
+	 */
+	avp->start += sample->v.start;
+	avp->end += sample->v.end;
+	avp->loopstart += sample->v.loopstart;
+	avp->loopend += sample->v.loopend;
+
+	/* copy mode flags */
+	avp->sample_mode = sample->v.mode_flags;
+
+	return sample;
+}
+
+/* find the sample pointer with the given id in the soundfont */
+static snd_sf_sample_t *
+find_sample(snd_soundfont_t *sf, int sample_id)
+{
+	snd_sf_sample_t *p;
+
+	if (sf == NULL)
+		return NULL;
+
+	for (p = sf->samples; p; p = p->next) {
+		if (p->v.sample == sample_id)
+			return p;
+	}
+	return NULL;
+}
+
+
+/*
+ * Load sample information, this can include data to be loaded onto
+ * the soundcard.  It can also just be a pointer into soundcard ROM.
+ * If there is data it will be written to the soundcard via the callback
+ * routine.
+ */
+static int
+load_data(snd_sf_list_t *sflist, const void __user *data, long count)
+{
+	snd_soundfont_t *sf;
+	soundfont_sample_info_t sample_info;
+	snd_sf_sample_t *sp;
+	long off;
+
+	/* patch must be opened */
+	if ((sf = sflist->currsf) == NULL)
+		return -EINVAL;
+
+	if (is_special_type(sf->type))
+		return -EINVAL;
+
+	if (copy_from_user(&sample_info, data, sizeof(sample_info)))
+		return -EFAULT;
+
+	off = sizeof(sample_info);
+
+	if (sample_info.size != (count-off)/2)
+		return -EINVAL;
+
+	/* Check for dup */
+	if (find_sample(sf, sample_info.sample)) {
+		/* if shared sample, skip this data */
+		if (sf->type & SNDRV_SFNT_PAT_SHARED)
+			return 0;
+		return -EINVAL;
+	}
+
+	/* Allocate a new sample structure */
+	if ((sp = sf_sample_new(sflist, sf)) == NULL)
+		return -ENOMEM;
+
+	sp->v = sample_info;
+	sp->v.sf_id = sf->id;
+	sp->v.dummy = 0;
+	sp->v.truesize = sp->v.size;
+
+	/*
+	 * If there is wave data then load it.
+	 */
+	if (sp->v.size > 0) {
+		int  rc;
+		rc = sflist->callback.sample_new
+			(sflist->callback.private_data, sp, sflist->memhdr,
+			 data + off, count - off);
+		if (rc < 0) {
+			sf_sample_delete(sflist, sf, sp);
+			return rc;
+		}
+		sflist->mem_used += sp->v.truesize;
+	}
+
+	return count;
+}
+
+
+/* log2_tbl[i] = log2(i+128) * 0x10000 */
+static int log_tbl[129] = {
+	0x70000, 0x702df, 0x705b9, 0x7088e, 0x70b5d, 0x70e26, 0x710eb, 0x713aa,
+	0x71663, 0x71918, 0x71bc8, 0x71e72, 0x72118, 0x723b9, 0x72655, 0x728ed,
+	0x72b80, 0x72e0e, 0x73098, 0x7331d, 0x7359e, 0x7381b, 0x73a93, 0x73d08,
+	0x73f78, 0x741e4, 0x7444c, 0x746b0, 0x74910, 0x74b6c, 0x74dc4, 0x75019,
+	0x75269, 0x754b6, 0x75700, 0x75946, 0x75b88, 0x75dc7, 0x76002, 0x7623a,
+	0x7646e, 0x766a0, 0x768cd, 0x76af8, 0x76d1f, 0x76f43, 0x77164, 0x77382,
+	0x7759d, 0x777b4, 0x779c9, 0x77bdb, 0x77dea, 0x77ff5, 0x781fe, 0x78404,
+	0x78608, 0x78808, 0x78a06, 0x78c01, 0x78df9, 0x78fef, 0x791e2, 0x793d2,
+	0x795c0, 0x797ab, 0x79993, 0x79b79, 0x79d5d, 0x79f3e, 0x7a11d, 0x7a2f9,
+	0x7a4d3, 0x7a6ab, 0x7a880, 0x7aa53, 0x7ac24, 0x7adf2, 0x7afbe, 0x7b188,
+	0x7b350, 0x7b515, 0x7b6d8, 0x7b899, 0x7ba58, 0x7bc15, 0x7bdd0, 0x7bf89,
+	0x7c140, 0x7c2f5, 0x7c4a7, 0x7c658, 0x7c807, 0x7c9b3, 0x7cb5e, 0x7cd07,
+	0x7ceae, 0x7d053, 0x7d1f7, 0x7d398, 0x7d538, 0x7d6d6, 0x7d872, 0x7da0c,
+	0x7dba4, 0x7dd3b, 0x7ded0, 0x7e063, 0x7e1f4, 0x7e384, 0x7e512, 0x7e69f,
+	0x7e829, 0x7e9b3, 0x7eb3a, 0x7ecc0, 0x7ee44, 0x7efc7, 0x7f148, 0x7f2c8,
+	0x7f446, 0x7f5c2, 0x7f73d, 0x7f8b7, 0x7fa2f, 0x7fba5, 0x7fd1a, 0x7fe8d,
+	0x80000,
+};
+
+/* convert from linear to log value
+ *
+ * conversion: value = log2(amount / base) * ratio
+ *
+ * argument:
+ *   amount = linear value (unsigned, 32bit max)
+ *   offset = base offset (:= log2(base) * 0x10000)
+ *   ratio = division ratio
+ *
+ */
+int
+snd_sf_linear_to_log(unsigned int amount, int offset, int ratio)
+{
+	int v;
+	int s, low, bit;
+	
+	if (amount < 2)
+		return 0;
+	for (bit = 0; ! (amount & 0x80000000L); bit++)
+		amount <<= 1;
+	s = (amount >> 24) & 0x7f;
+	low = (amount >> 16) & 0xff;
+	/* linear approxmimation by lower 8 bit */
+	v = (log_tbl[s + 1] * low + log_tbl[s] * (0x100 - low)) >> 8;
+	v -= offset;
+	v = (v * ratio) >> 16;
+	v += (24 - bit) * ratio;
+	return v;
+}
+
+#define OFFSET_MSEC		653117		/* base = 1000 */
+#define OFFSET_ABSCENT		851781		/* base = 8176 */
+#define OFFSET_SAMPLERATE	1011119		/* base = 44100 */
+
+#define ABSCENT_RATIO		1200
+#define TIMECENT_RATIO		1200
+#define SAMPLERATE_RATIO	4096
+
+/*
+ * mHz to abscent
+ * conversion: abscent = log2(MHz / 8176) * 1200
+ */
+static int
+freq_to_note(int mhz)
+{
+	return snd_sf_linear_to_log(mhz, OFFSET_ABSCENT, ABSCENT_RATIO);
+}
+
+/* convert Hz to AWE32 rate offset:
+ * sample pitch offset for the specified sample rate
+ * rate=44100 is no offset, each 4096 is 1 octave (twice).
+ * eg, when rate is 22050, this offset becomes -4096.
+ *
+ * conversion: offset = log2(Hz / 44100) * 4096
+ */
+static int
+calc_rate_offset(int hz)
+{
+	return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO);
+}
+
+
+/* calculate GUS envelope time */
+static int
+calc_gus_envelope_time(int rate, int start, int end)
+{
+	int r, p, t;
+	r = (3 - ((rate >> 6) & 3)) * 3;
+	p = rate & 0x3f;
+	t = end - start;
+	if (t < 0) t = -t;
+	if (13 > r)
+		t = t << (13 - r);
+	else
+		t = t >> (r - 13);
+	return (t * 10) / (p * 441);
+}
+
+/* convert envelope time parameter to soundfont parameters */
+
+/* attack & decay/release time table (msec) */
+static short attack_time_tbl[128] = {
+32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816,
+707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377,
+361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188,
+180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94,
+90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47,
+45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23,
+22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12,
+11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0,
+};
+
+static short decay_time_tbl[128] = {
+32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082,
+2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507,
+1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722,
+691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361,
+345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180,
+172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90,
+86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45,
+43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22,
+};
+
+/* delay time = 0x8000 - msec/92 */
+int
+snd_sf_calc_parm_hold(int msec)
+{
+	int val = (0x7f * 92 - msec) / 92;
+	if (val < 1) val = 1;
+	if (val >= 126) val = 126;
+	return val;
+}
+
+/* search an index for specified time from given time table */
+static int
+calc_parm_search(int msec, short *table)
+{
+	int left = 1, right = 127, mid;
+	while (left < right) {
+		mid = (left + right) / 2;
+		if (msec < (int)table[mid])
+			left = mid + 1;
+		else
+			right = mid;
+	}
+	return left;
+}
+
+/* attack time: search from time table */
+int
+snd_sf_calc_parm_attack(int msec)
+{
+	return calc_parm_search(msec, attack_time_tbl);
+}
+
+/* decay/release time: search from time table */
+int
+snd_sf_calc_parm_decay(int msec)
+{
+	return calc_parm_search(msec, decay_time_tbl);
+}
+
+int snd_sf_vol_table[128] = {
+	255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49,
+	47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32,
+	31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22,
+	22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16,
+	15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,
+	10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6,
+	6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3,
+	2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0,
+};
+
+
+#define calc_gus_sustain(val)  (0x7f - snd_sf_vol_table[(val)/2])
+#define calc_gus_attenuation(val)	snd_sf_vol_table[(val)/2]
+
+/* load GUS patch */
+static int
+load_guspatch(snd_sf_list_t *sflist, const char __user *data, long count, int client)
+{
+	struct patch_info patch;
+	snd_soundfont_t *sf;
+	snd_sf_zone_t *zone;
+	snd_sf_sample_t *smp;
+	int note, sample_id;
+	int rc;
+
+	if (count < (long)sizeof(patch)) {
+		snd_printk("patch record too small %ld\n", count);
+		return -EINVAL;
+	}
+	if (copy_from_user(&patch, data, sizeof(patch)))
+		return -EFAULT;
+	
+	count -= sizeof(patch);
+	data += sizeof(patch);
+
+	sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL);
+	if (sf == NULL)
+		return -ENOMEM;
+	if ((smp = sf_sample_new(sflist, sf)) == NULL)
+		return -ENOMEM;
+	sample_id = sflist->sample_counter;
+	smp->v.sample = sample_id;
+	smp->v.start = 0;
+	smp->v.end = patch.len;
+	smp->v.loopstart = patch.loop_start;
+	smp->v.loopend = patch.loop_end;
+	smp->v.size = patch.len;
+
+	/* set up mode flags */
+	smp->v.mode_flags = 0;
+	if (!(patch.mode & WAVE_16_BITS))
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_8BITS;
+	if (patch.mode & WAVE_UNSIGNED)
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_UNSIGNED;
+	smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_NO_BLANK;
+	if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_SINGLESHOT;
+	if (patch.mode & WAVE_BIDIR_LOOP)
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_BIDIR_LOOP;
+	if (patch.mode & WAVE_LOOP_BACK)
+		smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_REVERSE_LOOP;
+
+	if (patch.mode & WAVE_16_BITS) {
+		/* convert to word offsets */
+		smp->v.size /= 2;
+		smp->v.end /= 2;
+		smp->v.loopstart /= 2;
+		smp->v.loopend /= 2;
+	}
+	/*smp->v.loopend++;*/
+
+	smp->v.dummy = 0;
+	smp->v.truesize = 0;
+	smp->v.sf_id = sf->id;
+
+	/* set up voice info */
+	if ((zone = sf_zone_new(sflist, sf)) == NULL) {
+		sf_sample_delete(sflist, sf, smp);
+		return -ENOMEM;
+	}
+
+	/*
+	 * load wave data
+	 */
+	if (sflist->callback.sample_new) {
+		rc = sflist->callback.sample_new
+			(sflist->callback.private_data, smp, sflist->memhdr, data, count);
+		if (rc < 0) {
+			sf_sample_delete(sflist, sf, smp);
+			return rc;
+		}
+		/* memory offset is updated after */
+	}
+
+	/* update the memory offset here */
+	sflist->mem_used += smp->v.truesize;
+
+	zone->v.sample = sample_id; /* the last sample */
+	zone->v.rate_offset = calc_rate_offset(patch.base_freq);
+	note = freq_to_note(patch.base_note);
+	zone->v.root = note / 100;
+	zone->v.tune = -(note % 100);
+	zone->v.low = (freq_to_note(patch.low_note) + 99) / 100;
+	zone->v.high = freq_to_note(patch.high_note) / 100;
+	/* panning position; -128 - 127 => 0-127 */
+	zone->v.pan = (patch.panning + 128) / 2;
+#if 0
+	snd_printk("gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n",
+		   (int)patch.base_freq, zone->v.rate_offset,
+		   zone->v.root, zone->v.tune, zone->v.low, zone->v.high);
+#endif
+
+	/* detuning is ignored */
+	/* 6points volume envelope */
+	if (patch.mode & WAVE_ENVELOPES) {
+		int attack, hold, decay, release;
+		attack = calc_gus_envelope_time
+			(patch.env_rate[0], 0, patch.env_offset[0]);
+		hold = calc_gus_envelope_time
+			(patch.env_rate[1], patch.env_offset[0],
+			 patch.env_offset[1]);
+		decay = calc_gus_envelope_time
+			(patch.env_rate[2], patch.env_offset[1],
+			 patch.env_offset[2]);
+		release = calc_gus_envelope_time
+			(patch.env_rate[3], patch.env_offset[1],
+			 patch.env_offset[4]);
+		release += calc_gus_envelope_time
+			(patch.env_rate[4], patch.env_offset[3],
+			 patch.env_offset[4]);
+		release += calc_gus_envelope_time
+			(patch.env_rate[5], patch.env_offset[4],
+			 patch.env_offset[5]);
+		zone->v.parm.volatkhld = 
+			(snd_sf_calc_parm_hold(hold) << 8) |
+			snd_sf_calc_parm_attack(attack);
+		zone->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
+			snd_sf_calc_parm_decay(decay);
+		zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release);
+		zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]);
+#if 0
+		snd_printk("gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n",
+			   zone->v.parm.volatkhld,
+			   zone->v.parm.voldcysus,
+			   zone->v.parm.volrelease,
+			   zone->v.attenuation);
+#endif
+	}
+
+	/* fast release */
+	if (patch.mode & WAVE_FAST_RELEASE) {
+		zone->v.parm.volrelease = 0x807f;
+	}
+
+	/* tremolo effect */
+	if (patch.mode & WAVE_TREMOLO) {
+		int rate = (patch.tremolo_rate * 1000 / 38) / 42;
+		zone->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate;
+	}
+	/* vibrato effect */
+	if (patch.mode & WAVE_VIBRATO) {
+		int rate = (patch.vibrato_rate * 1000 / 38) / 42;
+		zone->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate;
+	}
+	
+	/* scale_freq, scale_factor, volume, and fractions not implemented */
+
+	if (!(smp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT))
+		zone->v.mode = SNDRV_SFNT_MODE_LOOPING;
+	else
+		zone->v.mode = 0;
+
+	/* append to the tail of the list */
+	/*zone->bank = ctrls[AWE_MD_GUS_BANK];*/
+	zone->bank = 0;
+	zone->instr = patch.instr_no;
+	zone->mapped = 0;
+	zone->v.sf_id = sf->id;
+
+	zone->sample = set_sample(sf, &zone->v);
+
+	/* rebuild preset now */
+	add_preset(sflist, zone);
+
+	return 0;
+}
+
+/* load GUS patch */
+int
+snd_soundfont_load_guspatch(snd_sf_list_t *sflist, const char __user *data,
+			    long count, int client)
+{
+	int rc;
+	lock_preset(sflist);
+	rc = load_guspatch(sflist, data, count, client);
+	unlock_preset(sflist);
+	return rc;
+}
+
+
+/*
+ * Rebuild the preset table.  This is like a hash table in that it allows
+ * quick access to the zone information.  For each preset there are zone
+ * structures linked by next_instr and by next_zone.  Former is the whole
+ * link for this preset, and latter is the link for zone (i.e. instrument/
+ * bank/key combination).
+ */
+static void
+rebuild_presets(snd_sf_list_t *sflist)
+{
+	snd_soundfont_t *sf;
+	snd_sf_zone_t *cur;
+
+	/* clear preset table */
+	memset(sflist->presets, 0, sizeof(sflist->presets));
+
+	/* search all fonts and insert each font */
+	for (sf = sflist->fonts; sf; sf = sf->next) {
+		for (cur = sf->zones; cur; cur = cur->next) {
+			if (! cur->mapped && cur->sample == NULL) {
+				/* try again to search the corresponding sample */
+				cur->sample = set_sample(sf, &cur->v);
+				if (cur->sample == NULL)
+					continue;
+			}
+
+			add_preset(sflist, cur);
+		}
+	}
+}
+
+
+/*
+ * add the given zone to preset table
+ */
+static void
+add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur)
+{
+	snd_sf_zone_t *zone;
+	int index;
+
+	zone = search_first_zone(sflist, cur->bank, cur->instr, cur->v.low);
+	if (zone && zone->v.sf_id != cur->v.sf_id) {
+		/* different instrument was already defined */
+		snd_sf_zone_t *p;
+		/* compare the allocated time */
+		for (p = zone; p; p = p->next_zone) {
+			if (p->counter > cur->counter)
+				/* the current is older.. skipped */
+				return;
+		}
+		/* remove old zones */
+		delete_preset(sflist, zone);
+		zone = NULL; /* do not forget to clear this! */
+	}
+
+	/* prepend this zone */
+	if ((index = get_index(cur->bank, cur->instr, cur->v.low)) < 0)
+		return;
+	cur->next_zone = zone; /* zone link */
+	cur->next_instr = sflist->presets[index]; /* preset table link */
+	sflist->presets[index] = cur;
+}
+
+/*
+ * delete the given zones from preset_table
+ */
+static void
+delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp)
+{
+	int index;
+	snd_sf_zone_t *p;
+
+	if ((index = get_index(zp->bank, zp->instr, zp->v.low)) < 0)
+		return;
+	for (p = sflist->presets[index]; p; p = p->next_instr) {
+		while (p->next_instr == zp) {
+			p->next_instr = zp->next_instr;
+			zp = zp->next_zone;
+			if (zp == NULL)
+				return;
+		}
+	}
+}
+
+
+/*
+ * Search matching zones from preset table.
+ * The note can be rewritten by preset mapping (alias).
+ * The found zones are stored on 'table' array.  max_layers defines
+ * the maximum number of elements in this array.
+ * This function returns the number of found zones.  0 if not found.
+ */
+int
+snd_soundfont_search_zone(snd_sf_list_t *sflist, int *notep, int vel,
+			  int preset, int bank,
+			  int def_preset, int def_bank,
+			  snd_sf_zone_t **table, int max_layers)
+{
+	int nvoices;
+	unsigned long flags;
+
+	/* this function is supposed to be called atomically,
+	 * so we check the lock.  if it's busy, just returns 0 to
+	 * tell the caller the busy state
+	 */
+	spin_lock_irqsave(&sflist->lock, flags);
+	if (sflist->presets_locked) {
+		spin_unlock_irqrestore(&sflist->lock, flags);
+		return 0;
+	}
+	nvoices = search_zones(sflist, notep, vel, preset, bank, table, max_layers, 0);
+	if (! nvoices) {
+		if (preset != def_preset || bank != def_bank)
+			nvoices = search_zones(sflist, notep, vel, def_preset, def_bank, table, max_layers, 0);
+	}
+	spin_unlock_irqrestore(&sflist->lock, flags);
+	return nvoices;
+}
+
+
+/*
+ * search the first matching zone
+ */
+static snd_sf_zone_t *
+search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key)
+{
+	int index;
+	snd_sf_zone_t *zp;
+
+	if ((index = get_index(bank, preset, key)) < 0)
+		return NULL;
+	for (zp = sflist->presets[index]; zp; zp = zp->next_instr) {
+		if (zp->instr == preset && zp->bank == bank)
+			return zp;
+	}
+	return NULL;
+}
+
+
+/*
+ * search matching zones from sflist.  can be called recursively.
+ */
+static int
+search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level)
+{
+	snd_sf_zone_t *zp;
+	int nvoices;
+
+	zp = search_first_zone(sflist, bank, preset, *notep);
+	nvoices = 0;
+	for (; zp; zp = zp->next_zone) {
+		if (*notep >= zp->v.low && *notep <= zp->v.high &&
+		    vel >= zp->v.vellow && vel <= zp->v.velhigh) {
+			if (zp->mapped) {
+				/* search preset mapping (aliasing) */
+				int key = zp->v.fixkey;
+				preset = zp->v.start;
+				bank = zp->v.end;
+
+				if (level > 5) /* too deep alias level */
+					return 0;
+				if (key < 0)
+					key = *notep;
+				nvoices = search_zones(sflist, &key, vel,
+						       preset, bank, table,
+						       max_layers, level + 1);
+				if (nvoices > 0)
+					*notep = key;
+				break;
+			}
+			table[nvoices++] = zp;
+			if (nvoices >= max_layers)
+				break;
+		}
+	}
+
+	return nvoices;
+}
+
+
+/* calculate the index of preset table:
+ * drums are mapped from 128 to 255 according to its note key.
+ * other instruments are mapped from 0 to 127.
+ * if the index is out of range, return -1.
+ */
+static int
+get_index(int bank, int instr, int key)
+{
+	int index;
+	if (SF_IS_DRUM_BANK(bank))
+		index = key + SF_MAX_INSTRUMENTS;
+	else
+		index = instr;
+	index = index % SF_MAX_PRESETS;
+	if (index < 0)
+		return -1;
+	return index;
+}
+
+/*
+ * Initialise the sflist structure.
+ */
+static void
+snd_sf_init(snd_sf_list_t *sflist)
+{
+	memset(sflist->presets, 0, sizeof(sflist->presets));
+
+	sflist->mem_used = 0;
+	sflist->currsf = NULL;
+	sflist->open_client = -1;
+	sflist->fonts = NULL;
+	sflist->fonts_size = 0;
+	sflist->zone_counter = 0;
+	sflist->sample_counter = 0;
+	sflist->zone_locked = 0;
+	sflist->sample_locked = 0;
+}
+
+/*
+ * Release all list records
+ */
+static void
+snd_sf_clear(snd_sf_list_t *sflist)
+{
+	snd_soundfont_t *sf, *nextsf;
+	snd_sf_zone_t *zp, *nextzp;
+	snd_sf_sample_t *sp, *nextsp;
+
+	for (sf = sflist->fonts; sf; sf = nextsf) {
+		nextsf = sf->next;
+		for (zp = sf->zones; zp; zp = nextzp) {
+			nextzp = zp->next;
+			kfree(zp);
+		}
+		for (sp = sf->samples; sp; sp = nextsp) {
+			nextsp = sp->next;
+			if (sflist->callback.sample_free)
+				sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr);
+			kfree(sp);
+		}
+		kfree(sf);
+	}
+
+	snd_sf_init(sflist);
+}
+
+
+/*
+ * Create a new sflist structure
+ */
+snd_sf_list_t *
+snd_sf_new(snd_sf_callback_t *callback, snd_util_memhdr_t *hdr)
+{
+	snd_sf_list_t *sflist;
+
+	if ((sflist = kcalloc(1, sizeof(*sflist), GFP_KERNEL)) == NULL)
+		return NULL;
+
+	init_MUTEX(&sflist->presets_mutex);
+	spin_lock_init(&sflist->lock);
+	sflist->memhdr = hdr;
+
+	if (callback)
+		sflist->callback = *callback;
+
+	snd_sf_init(sflist);
+	return sflist;
+}
+
+
+/*
+ * Free everything allocated off the sflist structure.
+ */
+void
+snd_sf_free(snd_sf_list_t *sflist)
+{
+	if (sflist == NULL)
+		return;
+	
+	lock_preset(sflist);
+	if (sflist->callback.sample_reset)
+		sflist->callback.sample_reset(sflist->callback.private_data);
+	snd_sf_clear(sflist);
+	unlock_preset(sflist);
+
+	kfree(sflist);
+}
+
+/*
+ * Remove all samples
+ * The soundcard should be silet before calling this function.
+ */
+int
+snd_soundfont_remove_samples(snd_sf_list_t *sflist)
+{
+	lock_preset(sflist);
+	if (sflist->callback.sample_reset)
+		sflist->callback.sample_reset(sflist->callback.private_data);
+	snd_sf_clear(sflist);
+	unlock_preset(sflist);
+
+	return 0;
+}
+
+/*
+ * Remove unlocked samples.
+ * The soundcard should be silent before calling this function.
+ */
+int
+snd_soundfont_remove_unlocked(snd_sf_list_t *sflist)
+{
+	snd_soundfont_t *sf;
+	snd_sf_zone_t *zp, *nextzp;
+	snd_sf_sample_t *sp, *nextsp;
+
+	lock_preset(sflist);
+
+	if (sflist->callback.sample_reset)
+		sflist->callback.sample_reset(sflist->callback.private_data);
+
+	/* to be sure */
+	memset(sflist->presets, 0, sizeof(sflist->presets));
+
+	for (sf = sflist->fonts; sf; sf = sf->next) {
+		for (zp = sf->zones; zp; zp = nextzp) {
+			if (zp->counter < sflist->zone_locked)
+				break;
+			nextzp = zp->next;
+			sf->zones = nextzp;
+			kfree(zp);
+		}
+
+		for (sp = sf->samples; sp; sp = nextsp) {
+			if (sp->counter < sflist->sample_locked)
+				break;
+			nextsp = sp->next;
+			sf->samples = nextsp;
+			sflist->mem_used -= sp->v.truesize;
+			if (sflist->callback.sample_free)
+				sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr);
+			kfree(sp);
+		}
+	}
+
+	sflist->zone_counter = sflist->zone_locked;
+	sflist->sample_counter = sflist->sample_locked;
+
+	rebuild_presets(sflist);
+
+	unlock_preset(sflist);
+	return 0;
+}
+