mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 16:58:04 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
12
sound/synth/Makefile
Normal file
12
sound/synth/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
#
|
||||
# Makefile for ALSA
|
||||
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
|
||||
#
|
||||
|
||||
snd-util-mem-objs := util_mem.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o
|
||||
obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o
|
||||
obj-$(CONFIG_SND_SBAWE_SEQ) += snd-util-mem.o
|
||||
obj-$(CONFIG_SND_SEQUENCER) += emux/
|
12
sound/synth/emux/Makefile
Normal file
12
sound/synth/emux/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
#
|
||||
# Makefile for ALSA
|
||||
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.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)
|
||||
|
||||
# Toplevel Module Dependencies
|
||||
obj-$(CONFIG_SND_SBAWE_SEQ) += snd-emux-synth.o
|
||||
obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-emux-synth.o
|
192
sound/synth/emux/emux.c
Normal file
192
sound/synth/emux/emux.c
Normal file
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* 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 <linux/wait.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/emux_synth.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.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(struct snd_emux **remu)
|
||||
{
|
||||
struct snd_emux *emu;
|
||||
|
||||
*remu = NULL;
|
||||
emu = kzalloc(sizeof(*emu), GFP_KERNEL);
|
||||
if (emu == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&emu->voice_lock);
|
||||
mutex_init(&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;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emux_new);
|
||||
|
||||
/*
|
||||
*/
|
||||
static int sf_sample_new(void *private_data, struct snd_sf_sample *sp,
|
||||
struct snd_util_memhdr *hdr,
|
||||
const void __user *buf, long count)
|
||||
{
|
||||
struct snd_emux *emu = private_data;
|
||||
return emu->ops.sample_new(emu, sp, hdr, buf, count);
|
||||
|
||||
}
|
||||
|
||||
static int sf_sample_free(void *private_data, struct snd_sf_sample *sp,
|
||||
struct snd_util_memhdr *hdr)
|
||||
{
|
||||
struct snd_emux *emu = private_data;
|
||||
return emu->ops.sample_free(emu, sp, hdr);
|
||||
|
||||
}
|
||||
|
||||
static void sf_sample_reset(void *private_data)
|
||||
{
|
||||
struct snd_emux *emu = private_data;
|
||||
emu->ops.sample_reset(emu);
|
||||
}
|
||||
|
||||
int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, char *name)
|
||||
{
|
||||
int err;
|
||||
struct snd_sf_callback sf_cb;
|
||||
|
||||
if (snd_BUG_ON(!emu->hw || emu->max_voices <= 0))
|
||||
return -EINVAL;
|
||||
if (snd_BUG_ON(!card || !name))
|
||||
return -EINVAL;
|
||||
|
||||
emu->card = card;
|
||||
emu->name = kstrdup(name, GFP_KERNEL);
|
||||
emu->voices = kcalloc(emu->max_voices, sizeof(struct snd_emux_voice),
|
||||
GFP_KERNEL);
|
||||
if (emu->voices == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* create soundfont list */
|
||||
memset(&sf_cb, 0, sizeof(sf_cb));
|
||||
sf_cb.private_data = emu;
|
||||
if (emu->ops.sample_new)
|
||||
sf_cb.sample_new = sf_sample_new;
|
||||
if (emu->ops.sample_free)
|
||||
sf_cb.sample_free = sf_sample_free;
|
||||
if (emu->ops.sample_reset)
|
||||
sf_cb.sample_reset = sf_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;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emux_register);
|
||||
|
||||
/*
|
||||
*/
|
||||
int snd_emux_free(struct snd_emux *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_free);
|
||||
|
||||
|
||||
/*
|
||||
* 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)
|
310
sound/synth/emux/emux_effect.c
Normal file
310
sound/synth/emux/emux_effect.c
Normal file
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* 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(struct soundfont_voice_parm *, 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, struct snd_midi_channel *chan, int type)
|
||||
{
|
||||
short effect;
|
||||
struct snd_emux_effect_table *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, struct snd_midi_channel *chan, int type)
|
||||
{
|
||||
int effect;
|
||||
struct snd_emux_effect_table *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(struct snd_midi_channel *chan, int lo, int hi, int mode)
|
||||
{
|
||||
int addr = 0;
|
||||
struct snd_emux_effect_table *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(struct snd_emux_port *port,
|
||||
struct snd_midi_channel *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(struct snd_emux_port *port, struct snd_midi_channel *chan,
|
||||
int type, int val, int mode)
|
||||
{
|
||||
int i;
|
||||
int offset;
|
||||
unsigned char *srcp, *origp;
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_effect_table *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++) {
|
||||
struct snd_emux_voice *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(struct snd_emux_voice *vp)
|
||||
{
|
||||
struct snd_midi_channel *chan = vp->chan;
|
||||
struct snd_emux_effect_table *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(struct snd_emux_port *p)
|
||||
{
|
||||
int i;
|
||||
p->effect = kcalloc(p->chset.max_channels,
|
||||
sizeof(struct snd_emux_effect_table), 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(struct snd_emux_port *p)
|
||||
{
|
||||
kfree(p->effect);
|
||||
p->effect = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
snd_emux_clear_effect(struct snd_emux_port *p)
|
||||
{
|
||||
if (p->effect) {
|
||||
memset(p->effect, 0, sizeof(struct snd_emux_effect_table) *
|
||||
p->chset.max_channels);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SNDRV_EMUX_USE_RAW_EFFECT */
|
153
sound/synth/emux/emux_hwdep.c
Normal file
153
sound/synth/emux/emux_hwdep.c
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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/core.h>
|
||||
#include <sound/hwdep.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "emux_voice.h"
|
||||
|
||||
|
||||
#define TMP_CLIENT_ID 0x1001
|
||||
|
||||
/*
|
||||
* load patch
|
||||
*/
|
||||
static int
|
||||
snd_emux_hwdep_load_patch(struct snd_emux *emu, void __user *arg)
|
||||
{
|
||||
int err;
|
||||
struct soundfont_patch_info 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(struct snd_emux *emu, void __user *arg)
|
||||
{
|
||||
struct snd_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(struct snd_hwdep * hw, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct snd_emux *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(struct snd_emux *emu)
|
||||
{
|
||||
struct snd_hwdep *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.ioctl = snd_emux_hwdep_ioctl;
|
||||
/* The ioctl parameter types are compatible between 32- and
|
||||
* 64-bit architectures, so use the same function. */
|
||||
hw->ops.ioctl_compat = 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(struct snd_emux *emu)
|
||||
{
|
||||
if (emu->hwdep) {
|
||||
snd_device_free(emu->card, emu->hwdep);
|
||||
emu->hwdep = NULL;
|
||||
}
|
||||
}
|
396
sound/synth/emux/emux_nrpn.c
Normal file
396
sound/synth/emux/emux_nrpn.c
Normal file
|
@ -0,0 +1,396 @@
|
|||
/*
|
||||
* 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 */
|
||||
struct nrpn_conv_table {
|
||||
int control;
|
||||
int effect;
|
||||
int (*convert)(int val);
|
||||
};
|
||||
|
||||
/* 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(struct nrpn_conv_table *table, int num_tables,
|
||||
struct snd_emux_port *port,
|
||||
struct snd_midi_channel *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 struct 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 struct 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, struct snd_midi_channel *chan,
|
||||
struct snd_midi_channel_set *chset)
|
||||
{
|
||||
struct snd_emux_port *port;
|
||||
|
||||
port = p;
|
||||
if (snd_BUG_ON(!port || !chan))
|
||||
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 struct 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(struct snd_emux_port *port, struct snd_midi_channel *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,
|
||||
struct snd_midi_channel_set *chset)
|
||||
{
|
||||
struct snd_emux_port *port;
|
||||
struct snd_emux *emu;
|
||||
|
||||
port = p;
|
||||
if (snd_BUG_ON(!port || !chset))
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
508
sound/synth/emux/emux_oss.c
Normal file
508
sound/synth/emux/emux_oss.c
Normal file
|
@ -0,0 +1,508 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef CONFIG_SND_SEQUENCER_OSS
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <sound/core.h>
|
||||
#include "emux_voice.h"
|
||||
#include <sound/asoundef.h>
|
||||
|
||||
static int snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure);
|
||||
static int snd_emux_close_seq_oss(struct snd_seq_oss_arg *arg);
|
||||
static int snd_emux_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
|
||||
unsigned long ioarg);
|
||||
static int snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
|
||||
const char __user *buf, int offs, int count);
|
||||
static int snd_emux_reset_seq_oss(struct snd_seq_oss_arg *arg);
|
||||
static int snd_emux_event_oss_input(struct snd_seq_event *ev, int direct,
|
||||
void *private, int atomic, int hop);
|
||||
static void reset_port_mode(struct snd_emux_port *port, int midi_mode);
|
||||
static void emuspec_control(struct snd_emux *emu, struct snd_emux_port *port,
|
||||
int cmd, unsigned char *event, int atomic, int hop);
|
||||
static void gusspec_control(struct snd_emux *emu, struct snd_emux_port *port,
|
||||
int cmd, unsigned char *event, int atomic, int hop);
|
||||
static void fake_event(struct snd_emux *emu, struct snd_emux_port *port,
|
||||
int ch, int param, int val, int atomic, int hop);
|
||||
|
||||
/* operators */
|
||||
static struct snd_seq_oss_callback 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(struct snd_emux *emu)
|
||||
{
|
||||
struct snd_seq_oss_reg *arg;
|
||||
struct snd_seq_device *dev;
|
||||
|
||||
if (snd_seq_device_new(emu->card, 0, SNDRV_SEQ_DEV_ID_OSS,
|
||||
sizeof(struct snd_seq_oss_reg), &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(struct snd_emux *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(struct snd_seq_oss_arg *arg, void *closure)
|
||||
{
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_port *p;
|
||||
struct snd_seq_port_callback callback;
|
||||
char tmpname[64];
|
||||
|
||||
emu = closure;
|
||||
if (snd_BUG_ON(!arg || !emu))
|
||||
return -ENXIO;
|
||||
|
||||
if (!snd_emux_inc_count(emu))
|
||||
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(KERN_ERR "can't create port\n");
|
||||
snd_emux_dec_count(emu);
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define DEFAULT_DRUM_FLAGS ((1<<9) | (1<<25))
|
||||
|
||||
/*
|
||||
* reset port mode
|
||||
*/
|
||||
static void
|
||||
reset_port_mode(struct snd_emux_port *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(struct snd_seq_oss_arg *arg)
|
||||
{
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_port *p;
|
||||
|
||||
if (snd_BUG_ON(!arg))
|
||||
return -ENXIO;
|
||||
p = arg->private_data;
|
||||
if (snd_BUG_ON(!p))
|
||||
return -ENXIO;
|
||||
|
||||
emu = p->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
return -ENXIO;
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* load patch
|
||||
*/
|
||||
static int
|
||||
snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
|
||||
const char __user *buf, int offs, int count)
|
||||
{
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_port *p;
|
||||
int rc;
|
||||
|
||||
if (snd_BUG_ON(!arg))
|
||||
return -ENXIO;
|
||||
p = arg->private_data;
|
||||
if (snd_BUG_ON(!p))
|
||||
return -ENXIO;
|
||||
|
||||
emu = p->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
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) {
|
||||
struct soundfont_patch_info 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(struct snd_seq_oss_arg *arg, unsigned int cmd, unsigned long ioarg)
|
||||
{
|
||||
struct snd_emux_port *p;
|
||||
struct snd_emux *emu;
|
||||
|
||||
if (snd_BUG_ON(!arg))
|
||||
return -ENXIO;
|
||||
p = arg->private_data;
|
||||
if (snd_BUG_ON(!p))
|
||||
return -ENXIO;
|
||||
|
||||
emu = p->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
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(struct snd_seq_oss_arg *arg)
|
||||
{
|
||||
struct snd_emux_port *p;
|
||||
|
||||
if (snd_BUG_ON(!arg))
|
||||
return -ENXIO;
|
||||
p = arg->private_data;
|
||||
if (snd_BUG_ON(!p))
|
||||
return -ENXIO;
|
||||
snd_emux_reset_port(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* receive raw events: only SEQ_PRIVATE is accepted.
|
||||
*/
|
||||
static int
|
||||
snd_emux_event_oss_input(struct snd_seq_event *ev, int direct, void *private_data,
|
||||
int atomic, int hop)
|
||||
{
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_port *p;
|
||||
unsigned char cmd, *data;
|
||||
|
||||
p = private_data;
|
||||
if (snd_BUG_ON(!p))
|
||||
return -EINVAL;
|
||||
emu = p->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
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(struct snd_emux *emu, struct snd_emux_port *port, int cmd,
|
||||
unsigned char *event, int atomic, int hop)
|
||||
{
|
||||
int voice;
|
||||
unsigned short p1;
|
||||
short p2;
|
||||
int i;
|
||||
struct snd_midi_channel *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(struct snd_emux *emu, struct snd_emux_port *port, int cmd,
|
||||
unsigned char *event, int atomic, int hop)
|
||||
{
|
||||
int voice;
|
||||
unsigned short p1;
|
||||
short p2;
|
||||
int plong;
|
||||
struct snd_midi_channel *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(struct snd_emux *emu, struct snd_emux_port *port, int ch, int param, int val, int atomic, int hop)
|
||||
{
|
||||
struct snd_seq_event 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 */
|
132
sound/synth/emux/emux_proc.c
Normal file
132
sound/synth/emux/emux_proc.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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 <linux/wait.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(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buf)
|
||||
{
|
||||
struct snd_emux *emu;
|
||||
int i;
|
||||
|
||||
emu = entry->private_data;
|
||||
mutex_lock(&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) {
|
||||
mutex_lock(&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);
|
||||
mutex_unlock(&emu->sflist->presets_mutex);
|
||||
}
|
||||
#if 0 /* debug */
|
||||
if (emu->voices[0].state != SNDRV_EMUX_ST_OFF && emu->voices[0].ch >= 0) {
|
||||
struct snd_emux_voice *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
|
||||
mutex_unlock(&emu->register_mutex);
|
||||
}
|
||||
|
||||
|
||||
void snd_emux_proc_init(struct snd_emux *emu, struct snd_card *card, int device)
|
||||
{
|
||||
struct snd_info_entry *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 = 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(struct snd_emux *emu)
|
||||
{
|
||||
snd_info_free_entry(emu->proc);
|
||||
emu->proc = NULL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
416
sound/synth/emux/emux_seq.c
Normal file
416
sound/synth/emux/emux_seq.c
Normal file
|
@ -0,0 +1,416 @@
|
|||
/*
|
||||
* 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>
|
||||
#include <linux/module.h>
|
||||
|
||||
/* Prototypes for static functions */
|
||||
static void free_port(void *private);
|
||||
static void snd_emux_init_port(struct snd_emux_port *p);
|
||||
static int snd_emux_use(void *private_data, struct snd_seq_port_subscribe *info);
|
||||
static int snd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *info);
|
||||
|
||||
/*
|
||||
* MIDI emulation operators
|
||||
*/
|
||||
static struct snd_midi_op 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_HARDWARE |\
|
||||
SNDRV_SEQ_PORT_TYPE_SYNTHESIZER)
|
||||
|
||||
/*
|
||||
* 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(struct snd_emux *emu, struct snd_card *card, int index)
|
||||
{
|
||||
int i;
|
||||
struct snd_seq_port_callback pinfo;
|
||||
char tmpname[64];
|
||||
|
||||
emu->client = snd_seq_create_kernel_client(card, index,
|
||||
"%s WaveTable", emu->name);
|
||||
if (emu->client < 0) {
|
||||
snd_printk(KERN_ERR "can't create client\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (emu->num_ports < 0) {
|
||||
snd_printk(KERN_WARNING "seqports must be greater than zero\n");
|
||||
emu->num_ports = 1;
|
||||
} else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) {
|
||||
snd_printk(KERN_WARNING "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++) {
|
||||
struct snd_emux_port *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(KERN_ERR "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(struct snd_emux *emu)
|
||||
{
|
||||
if (emu->voices)
|
||||
snd_emux_terminate_all(emu);
|
||||
|
||||
if (emu->client >= 0) {
|
||||
snd_seq_delete_kernel_client(emu->client);
|
||||
emu->client = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* create a sequencer port and channel_set
|
||||
*/
|
||||
|
||||
struct snd_emux_port *
|
||||
snd_emux_create_port(struct snd_emux *emu, char *name,
|
||||
int max_channels, int oss_port,
|
||||
struct snd_seq_port_callback *callback)
|
||||
{
|
||||
struct snd_emux_port *p;
|
||||
int i, type, cap;
|
||||
|
||||
/* Allocate structures for this channel */
|
||||
if ((p = kzalloc(sizeof(*p), GFP_KERNEL)) == NULL) {
|
||||
snd_printk(KERN_ERR "no memory\n");
|
||||
return NULL;
|
||||
}
|
||||
p->chset.channels = kcalloc(max_channels, sizeof(struct snd_midi_channel), GFP_KERNEL);
|
||||
if (p->chset.channels == NULL) {
|
||||
snd_printk(KERN_ERR "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)
|
||||
{
|
||||
struct snd_emux_port *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(struct snd_emux_port *p)
|
||||
{
|
||||
p->drum_flags = DEFAULT_DRUM_FLAGS;
|
||||
p->volume_atten = 0;
|
||||
|
||||
snd_emux_reset_port(p);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* reset port
|
||||
*/
|
||||
void
|
||||
snd_emux_reset_port(struct snd_emux_port *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++) {
|
||||
struct snd_midi_channel *chan = port->chset.channels + i;
|
||||
chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* input sequencer event
|
||||
*/
|
||||
int
|
||||
snd_emux_event_input(struct snd_seq_event *ev, int direct, void *private_data,
|
||||
int atomic, int hop)
|
||||
{
|
||||
struct snd_emux_port *port;
|
||||
|
||||
port = private_data;
|
||||
if (snd_BUG_ON(!port || !ev))
|
||||
return -EINVAL;
|
||||
|
||||
snd_midi_process_event(&emux_ops, ev, &port->chset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* increment usage count
|
||||
*/
|
||||
static int
|
||||
__snd_emux_inc_count(struct snd_emux *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;
|
||||
}
|
||||
|
||||
int snd_emux_inc_count(struct snd_emux *emu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&emu->register_mutex);
|
||||
ret = __snd_emux_inc_count(emu);
|
||||
mutex_unlock(&emu->register_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* decrease usage count
|
||||
*/
|
||||
static void
|
||||
__snd_emux_dec_count(struct snd_emux *emu)
|
||||
{
|
||||
module_put(emu->card->module);
|
||||
emu->used--;
|
||||
if (emu->used <= 0)
|
||||
snd_emux_terminate_all(emu);
|
||||
module_put(emu->ops.owner);
|
||||
}
|
||||
|
||||
void snd_emux_dec_count(struct snd_emux *emu)
|
||||
{
|
||||
mutex_lock(&emu->register_mutex);
|
||||
__snd_emux_dec_count(emu);
|
||||
mutex_unlock(&emu->register_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine that is called upon a first use of a particular port
|
||||
*/
|
||||
static int
|
||||
snd_emux_use(void *private_data, struct snd_seq_port_subscribe *info)
|
||||
{
|
||||
struct snd_emux_port *p;
|
||||
struct snd_emux *emu;
|
||||
|
||||
p = private_data;
|
||||
if (snd_BUG_ON(!p))
|
||||
return -EINVAL;
|
||||
emu = p->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&emu->register_mutex);
|
||||
snd_emux_init_port(p);
|
||||
__snd_emux_inc_count(emu);
|
||||
mutex_unlock(&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, struct snd_seq_port_subscribe *info)
|
||||
{
|
||||
struct snd_emux_port *p;
|
||||
struct snd_emux *emu;
|
||||
|
||||
p = private_data;
|
||||
if (snd_BUG_ON(!p))
|
||||
return -EINVAL;
|
||||
emu = p->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&emu->register_mutex);
|
||||
snd_emux_sounds_off_all(p);
|
||||
__snd_emux_dec_count(emu);
|
||||
mutex_unlock(&emu->register_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* attach virtual rawmidi devices
|
||||
*/
|
||||
int snd_emux_init_virmidi(struct snd_emux *emu, struct snd_card *card)
|
||||
{
|
||||
int i;
|
||||
|
||||
emu->vmidi = NULL;
|
||||
if (emu->midi_ports <= 0)
|
||||
return 0;
|
||||
|
||||
emu->vmidi = kcalloc(emu->midi_ports, sizeof(struct snd_rawmidi *), GFP_KERNEL);
|
||||
if (emu->vmidi == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < emu->midi_ports; i++) {
|
||||
struct snd_rawmidi *rmidi;
|
||||
struct snd_virmidi_dev *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(KERN_DEBUG "virmidi %d ok\n", i); */
|
||||
}
|
||||
return 0;
|
||||
|
||||
__error:
|
||||
/* snd_printk(KERN_DEBUG "error init..\n"); */
|
||||
snd_emux_delete_virmidi(emu);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int snd_emux_delete_virmidi(struct snd_emux *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;
|
||||
}
|
984
sound/synth/emux/emux_synth.c
Normal file
984
sound/synth/emux/emux_synth.c
Normal file
|
@ -0,0 +1,984 @@
|
|||
/*
|
||||
* 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 <linux/export.h>
|
||||
#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(struct snd_emux *emu, struct snd_emux_port *port,
|
||||
int *notep, int vel, struct snd_midi_channel *chan,
|
||||
struct snd_sf_zone **table);
|
||||
static int get_bank(struct snd_emux_port *port, struct snd_midi_channel *chan);
|
||||
static void terminate_note1(struct snd_emux *emu, int note,
|
||||
struct snd_midi_channel *chan, int free);
|
||||
static void exclusive_note_off(struct snd_emux *emu, struct snd_emux_port *port,
|
||||
int exclass);
|
||||
static void terminate_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int free);
|
||||
static void update_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int update);
|
||||
static void setup_voice(struct snd_emux_voice *vp);
|
||||
static int calc_pan(struct snd_emux_voice *vp);
|
||||
static int calc_volume(struct snd_emux_voice *vp);
|
||||
static int calc_pitch(struct snd_emux_voice *vp);
|
||||
|
||||
|
||||
/*
|
||||
* Start a note.
|
||||
*/
|
||||
void
|
||||
snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
|
||||
{
|
||||
struct snd_emux *emu;
|
||||
int i, key, nvoices;
|
||||
struct snd_emux_voice *vp;
|
||||
struct snd_sf_zone *table[SNDRV_EMUX_MAX_MULTI_VOICES];
|
||||
unsigned long flags;
|
||||
struct snd_emux_port *port;
|
||||
|
||||
port = p;
|
||||
if (snd_BUG_ON(!port || !chan))
|
||||
return;
|
||||
|
||||
emu = port->emu;
|
||||
if (snd_BUG_ON(!emu || !emu->ops.get_voice || !emu->ops.trigger))
|
||||
return;
|
||||
|
||||
key = note; /* remember the original note */
|
||||
nvoices = get_zone(emu, port, ¬e, vel, chan, table);
|
||||
if (! nvoices)
|
||||
return;
|
||||
|
||||
/* exclusive note off */
|
||||
for (i = 0; i < nvoices; i++) {
|
||||
struct snd_sf_zone *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;
|
||||
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 */
|
||||
struct snd_emux_effect_table *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, struct snd_midi_channel *chan)
|
||||
{
|
||||
int ch;
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_voice *vp;
|
||||
unsigned long flags;
|
||||
struct snd_emux_port *port;
|
||||
|
||||
port = p;
|
||||
if (snd_BUG_ON(!port || !chan))
|
||||
return;
|
||||
|
||||
emu = port->emu;
|
||||
if (snd_BUG_ON(!emu || !emu->ops.release))
|
||||
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->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)
|
||||
{
|
||||
struct snd_emux *emu = (struct snd_emux *) data;
|
||||
struct snd_emux_voice *vp;
|
||||
unsigned long flags;
|
||||
int ch, do_again = 0;
|
||||
|
||||
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_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_irqrestore(&emu->voice_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* key pressure change
|
||||
*/
|
||||
void
|
||||
snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
|
||||
{
|
||||
int ch;
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_voice *vp;
|
||||
unsigned long flags;
|
||||
struct snd_emux_port *port;
|
||||
|
||||
port = p;
|
||||
if (snd_BUG_ON(!port || !chan))
|
||||
return;
|
||||
|
||||
emu = port->emu;
|
||||
if (snd_BUG_ON(!emu || !emu->ops.update))
|
||||
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(struct snd_emux_port *port, struct snd_midi_channel *chan, int update)
|
||||
{
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_voice *vp;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
if (! update)
|
||||
return;
|
||||
|
||||
emu = port->emu;
|
||||
if (snd_BUG_ON(!emu || !emu->ops.update))
|
||||
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(struct snd_emux_port *port, int update)
|
||||
{
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_voice *vp;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
if (! update)
|
||||
return;
|
||||
|
||||
emu = port->emu;
|
||||
if (snd_BUG_ON(!emu || !emu->ops.update))
|
||||
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 controller type event. This includes all types of
|
||||
* control events, not just the midi controllers
|
||||
*/
|
||||
void
|
||||
snd_emux_control(void *p, int type, struct snd_midi_channel *chan)
|
||||
{
|
||||
struct snd_emux_port *port;
|
||||
|
||||
port = p;
|
||||
if (snd_BUG_ON(!port || !chan))
|
||||
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 */
|
||||
if (chan->control[type] >= 64)
|
||||
snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, -160,
|
||||
EMUX_FX_FLAG_ADD);
|
||||
else
|
||||
snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, 0,
|
||||
EMUX_FX_FLAG_OFF);
|
||||
#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(struct snd_emux *emu, int note, struct snd_midi_channel *chan, int free)
|
||||
{
|
||||
int i;
|
||||
struct snd_emux_voice *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, struct snd_midi_channel *chan)
|
||||
{
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_port *port;
|
||||
|
||||
port = p;
|
||||
if (snd_BUG_ON(!port || !chan))
|
||||
return;
|
||||
|
||||
emu = port->emu;
|
||||
if (snd_BUG_ON(!emu || !emu->ops.terminate))
|
||||
return;
|
||||
|
||||
terminate_note1(emu, note, chan, 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Terminate all the notes
|
||||
*/
|
||||
void
|
||||
snd_emux_terminate_all(struct snd_emux *emu)
|
||||
{
|
||||
int i;
|
||||
struct snd_emux_voice *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);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emux_terminate_all);
|
||||
|
||||
/*
|
||||
* Terminate all voices associated with the given port
|
||||
*/
|
||||
void
|
||||
snd_emux_sounds_off_all(struct snd_emux_port *port)
|
||||
{
|
||||
int i;
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_voice *vp;
|
||||
unsigned long flags;
|
||||
|
||||
if (snd_BUG_ON(!port))
|
||||
return;
|
||||
emu = port->emu;
|
||||
if (snd_BUG_ON(!emu || !emu->ops.terminate))
|
||||
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(struct snd_emux *emu, struct snd_emux_port *port, int exclass)
|
||||
{
|
||||
struct snd_emux_voice *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(struct snd_emux *emu, struct snd_emux_voice *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(struct snd_emux *emu, struct snd_emux_voice *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(struct snd_emux_voice *vp)
|
||||
{
|
||||
struct soundfont_voice_parm *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(struct snd_emux_voice *vp)
|
||||
{
|
||||
struct snd_midi_channel *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(struct snd_emux_voice *vp)
|
||||
{
|
||||
int vol;
|
||||
int main_vol, expression_vol, master_vol;
|
||||
struct snd_midi_channel *chan = vp->chan;
|
||||
struct snd_emux_port *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) {
|
||||
struct snd_emux_effect_table *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(struct snd_emux_voice *vp)
|
||||
{
|
||||
struct snd_midi_channel *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) {
|
||||
struct snd_emux_effect_table *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(struct snd_emux_port *port, struct snd_midi_channel *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(struct snd_emux *emu, struct snd_emux_port *port,
|
||||
int *notep, int vel, struct snd_midi_channel *chan,
|
||||
struct snd_sf_zone **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(struct snd_emux *emu)
|
||||
{
|
||||
struct snd_emux_voice *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(struct snd_emux *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(KERN_WARNING
|
||||
"invalid voice for lock %d (state = %x)\n",
|
||||
voice, emu->voices[voice].state);
|
||||
spin_unlock_irqrestore(&emu->voice_lock, flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emux_lock_voice);
|
||||
|
||||
/*
|
||||
*/
|
||||
void snd_emux_unlock_voice(struct snd_emux *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(KERN_WARNING
|
||||
"invalid voice for unlock %d (state = %x)\n",
|
||||
voice, emu->voices[voice].state);
|
||||
spin_unlock_irqrestore(&emu->voice_lock, flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emux_unlock_voice);
|
96
sound/synth/emux/emux_voice.h
Normal file
96
sound/synth/emux/emux_voice.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
#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 <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(struct snd_emux *emu, struct snd_card *card, int index);
|
||||
void snd_emux_detach_seq(struct snd_emux *emu);
|
||||
struct snd_emux_port *snd_emux_create_port(struct snd_emux *emu, char *name,
|
||||
int max_channels, int type,
|
||||
struct snd_seq_port_callback *callback);
|
||||
void snd_emux_reset_port(struct snd_emux_port *port);
|
||||
int snd_emux_event_input(struct snd_seq_event *ev, int direct, void *private,
|
||||
int atomic, int hop);
|
||||
int snd_emux_inc_count(struct snd_emux *emu);
|
||||
void snd_emux_dec_count(struct snd_emux *emu);
|
||||
int snd_emux_init_virmidi(struct snd_emux *emu, struct snd_card *card);
|
||||
int snd_emux_delete_virmidi(struct snd_emux *emu);
|
||||
|
||||
/* Prototypes for emux_synth.c */
|
||||
void snd_emux_init_voices(struct snd_emux *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, struct snd_midi_channel *chan);
|
||||
void snd_emux_control(void *p, int type, struct snd_midi_channel *chan);
|
||||
|
||||
void snd_emux_sounds_off_all(struct snd_emux_port *port);
|
||||
void snd_emux_update_channel(struct snd_emux_port *port,
|
||||
struct snd_midi_channel *chan, int update);
|
||||
void snd_emux_update_port(struct snd_emux_port *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(struct snd_emux_port *p);
|
||||
void snd_emux_delete_effect(struct snd_emux_port *p);
|
||||
void snd_emux_clear_effect(struct snd_emux_port *p);
|
||||
void snd_emux_setup_effect(struct snd_emux_voice *vp);
|
||||
void snd_emux_send_effect_oss(struct snd_emux_port *port,
|
||||
struct snd_midi_channel *chan, int type, int val);
|
||||
void snd_emux_send_effect(struct snd_emux_port *port,
|
||||
struct snd_midi_channel *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, struct snd_midi_channel_set *chset);
|
||||
int snd_emux_xg_control(struct snd_emux_port *port,
|
||||
struct snd_midi_channel *chan, int param);
|
||||
void snd_emux_nrpn(void *private_data, struct snd_midi_channel *chan,
|
||||
struct snd_midi_channel_set *chset);
|
||||
|
||||
/* emux_oss.c */
|
||||
void snd_emux_init_seq_oss(struct snd_emux *emu);
|
||||
void snd_emux_detach_seq_oss(struct snd_emux *emu);
|
||||
|
||||
/* emux_proc.c */
|
||||
#ifdef CONFIG_PROC_FS
|
||||
void snd_emux_proc_init(struct snd_emux *emu, struct snd_card *card, int device);
|
||||
void snd_emux_proc_free(struct snd_emux *emu);
|
||||
#endif
|
||||
|
||||
#define STATE_IS_PLAYING(s) ((s) & SNDRV_EMUX_ST_ON)
|
||||
|
||||
/* emux_hwdep.c */
|
||||
int snd_emux_init_hwdep(struct snd_emux *emu);
|
||||
void snd_emux_delete_hwdep(struct snd_emux *emu);
|
||||
|
||||
#endif
|
1497
sound/synth/emux/soundfont.c
Normal file
1497
sound/synth/emux/soundfont.c
Normal file
File diff suppressed because it is too large
Load diff
211
sound/synth/util_mem.c
Normal file
211
sound/synth/util_mem.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* Generic memory management routines for soundcard memory allocation
|
||||
*
|
||||
* 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 <linux/mutex.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/util_mem.h>
|
||||
|
||||
MODULE_AUTHOR("Takashi Iwai");
|
||||
MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define get_memblk(p) list_entry(p, struct snd_util_memblk, list)
|
||||
|
||||
/*
|
||||
* create a new memory manager
|
||||
*/
|
||||
struct snd_util_memhdr *
|
||||
snd_util_memhdr_new(int memsize)
|
||||
{
|
||||
struct snd_util_memhdr *hdr;
|
||||
|
||||
hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
|
||||
if (hdr == NULL)
|
||||
return NULL;
|
||||
hdr->size = memsize;
|
||||
mutex_init(&hdr->block_mutex);
|
||||
INIT_LIST_HEAD(&hdr->block);
|
||||
|
||||
return hdr;
|
||||
}
|
||||
|
||||
/*
|
||||
* free a memory manager
|
||||
*/
|
||||
void snd_util_memhdr_free(struct snd_util_memhdr *hdr)
|
||||
{
|
||||
struct list_head *p;
|
||||
|
||||
if (!hdr)
|
||||
return;
|
||||
/* release all blocks */
|
||||
while ((p = hdr->block.next) != &hdr->block) {
|
||||
list_del(p);
|
||||
kfree(get_memblk(p));
|
||||
}
|
||||
kfree(hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a memory block (without mutex)
|
||||
*/
|
||||
struct snd_util_memblk *
|
||||
__snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size)
|
||||
{
|
||||
struct snd_util_memblk *blk;
|
||||
unsigned int units, prev_offset;
|
||||
struct list_head *p;
|
||||
|
||||
if (snd_BUG_ON(!hdr || size <= 0))
|
||||
return NULL;
|
||||
|
||||
/* word alignment */
|
||||
units = size;
|
||||
if (units & 1)
|
||||
units++;
|
||||
if (units > hdr->size)
|
||||
return NULL;
|
||||
|
||||
/* look for empty block */
|
||||
prev_offset = 0;
|
||||
list_for_each(p, &hdr->block) {
|
||||
blk = get_memblk(p);
|
||||
if (blk->offset - prev_offset >= units)
|
||||
goto __found;
|
||||
prev_offset = blk->offset + blk->size;
|
||||
}
|
||||
if (hdr->size - prev_offset < units)
|
||||
return NULL;
|
||||
|
||||
__found:
|
||||
return __snd_util_memblk_new(hdr, units, p->prev);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* create a new memory block with the given size
|
||||
* the block is linked next to prev
|
||||
*/
|
||||
struct snd_util_memblk *
|
||||
__snd_util_memblk_new(struct snd_util_memhdr *hdr, unsigned int units,
|
||||
struct list_head *prev)
|
||||
{
|
||||
struct snd_util_memblk *blk;
|
||||
|
||||
blk = kmalloc(sizeof(struct snd_util_memblk) + hdr->block_extra_size,
|
||||
GFP_KERNEL);
|
||||
if (blk == NULL)
|
||||
return NULL;
|
||||
|
||||
if (prev == &hdr->block)
|
||||
blk->offset = 0;
|
||||
else {
|
||||
struct snd_util_memblk *p = get_memblk(prev);
|
||||
blk->offset = p->offset + p->size;
|
||||
}
|
||||
blk->size = units;
|
||||
list_add(&blk->list, prev);
|
||||
hdr->nblocks++;
|
||||
hdr->used += units;
|
||||
return blk;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* allocate a memory block (with mutex)
|
||||
*/
|
||||
struct snd_util_memblk *
|
||||
snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size)
|
||||
{
|
||||
struct snd_util_memblk *blk;
|
||||
mutex_lock(&hdr->block_mutex);
|
||||
blk = __snd_util_mem_alloc(hdr, size);
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return blk;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* remove the block from linked-list and free resource
|
||||
* (without mutex)
|
||||
*/
|
||||
void
|
||||
__snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)
|
||||
{
|
||||
list_del(&blk->list);
|
||||
hdr->nblocks--;
|
||||
hdr->used -= blk->size;
|
||||
kfree(blk);
|
||||
}
|
||||
|
||||
/*
|
||||
* free a memory block (with mutex)
|
||||
*/
|
||||
int snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)
|
||||
{
|
||||
if (snd_BUG_ON(!hdr || !blk))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&hdr->block_mutex);
|
||||
__snd_util_mem_free(hdr, blk);
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* return available memory size
|
||||
*/
|
||||
int snd_util_mem_avail(struct snd_util_memhdr *hdr)
|
||||
{
|
||||
unsigned int size;
|
||||
mutex_lock(&hdr->block_mutex);
|
||||
size = hdr->size - hdr->used;
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(snd_util_memhdr_new);
|
||||
EXPORT_SYMBOL(snd_util_memhdr_free);
|
||||
EXPORT_SYMBOL(snd_util_mem_alloc);
|
||||
EXPORT_SYMBOL(snd_util_mem_free);
|
||||
EXPORT_SYMBOL(snd_util_mem_avail);
|
||||
EXPORT_SYMBOL(__snd_util_mem_alloc);
|
||||
EXPORT_SYMBOL(__snd_util_mem_free);
|
||||
EXPORT_SYMBOL(__snd_util_memblk_new);
|
||||
|
||||
/*
|
||||
* INIT part
|
||||
*/
|
||||
|
||||
static int __init alsa_util_mem_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alsa_util_mem_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(alsa_util_mem_init)
|
||||
module_exit(alsa_util_mem_exit)
|
Loading…
Add table
Add a link
Reference in a new issue