diff options
Diffstat (limited to 'sound/synth')
-rw-r--r-- | sound/synth/Makefile | 20 | ||||
-rw-r--r-- | sound/synth/emux/Makefile | 20 | ||||
-rw-r--r-- | sound/synth/emux/emux.c | 173 | ||||
-rw-r--r-- | sound/synth/emux/emux_effect.c | 308 | ||||
-rw-r--r-- | sound/synth/emux/emux_hwdep.c | 171 | ||||
-rw-r--r-- | sound/synth/emux/emux_nrpn.c | 392 | ||||
-rw-r--r-- | sound/synth/emux/emux_oss.c | 497 | ||||
-rw-r--r-- | sound/synth/emux/emux_proc.c | 138 | ||||
-rw-r--r-- | sound/synth/emux/emux_seq.c | 428 | ||||
-rw-r--r-- | sound/synth/emux/emux_synth.c | 963 | ||||
-rw-r--r-- | sound/synth/emux/emux_voice.h | 88 | ||||
-rw-r--r-- | sound/synth/emux/soundfont.c | 1462 | ||||
-rw-r--r-- | sound/synth/util_mem.c | 207 |
13 files changed, 4867 insertions, 0 deletions
diff --git a/sound/synth/Makefile b/sound/synth/Makefile new file mode 100644 index 000000000000..986291dcb914 --- /dev/null +++ b/sound/synth/Makefile | |||
@@ -0,0 +1,20 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-util-mem-objs := util_mem.o | ||
7 | |||
8 | # | ||
9 | # this function returns: | ||
10 | # "m" - CONFIG_SND_SEQUENCER is m | ||
11 | # <empty string> - CONFIG_SND_SEQUENCER is undefined | ||
12 | # otherwise parameter #1 value | ||
13 | # | ||
14 | sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) | ||
15 | |||
16 | # Toplevel Module Dependency | ||
17 | obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o | ||
18 | obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o | ||
19 | obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-util-mem.o | ||
20 | obj-$(call sequencer,$(CONFIG_SND)) += emux/ | ||
diff --git a/sound/synth/emux/Makefile b/sound/synth/emux/Makefile new file mode 100644 index 000000000000..32a102d26709 --- /dev/null +++ b/sound/synth/emux/Makefile | |||
@@ -0,0 +1,20 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \ | ||
7 | emux_effect.o emux_proc.o emux_hwdep.o soundfont.o \ | ||
8 | $(if $(CONFIG_SND_SEQUENCER_OSS),emux_oss.o) | ||
9 | |||
10 | # | ||
11 | # this function returns: | ||
12 | # "m" - CONFIG_SND_SEQUENCER is m | ||
13 | # <empty string> - CONFIG_SND_SEQUENCER is undefined | ||
14 | # otherwise parameter #1 value | ||
15 | # | ||
16 | sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) | ||
17 | |||
18 | # Toplevel Module Dependencies | ||
19 | obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emux-synth.o | ||
20 | obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-emux-synth.o | ||
diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c new file mode 100644 index 000000000000..16f3b461627a --- /dev/null +++ b/sound/synth/emux/emux.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de> | ||
3 | * | ||
4 | * Routines for control of EMU WaveTable chip | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/wait.h> | ||
23 | #include <linux/sched.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/emux_synth.h> | ||
27 | #include <linux/init.h> | ||
28 | #include "emux_voice.h" | ||
29 | |||
30 | MODULE_AUTHOR("Takashi Iwai"); | ||
31 | MODULE_DESCRIPTION("Routines for control of EMU WaveTable chip"); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | /* | ||
35 | * create a new hardware dependent device for Emu8000/Emu10k1 | ||
36 | */ | ||
37 | int snd_emux_new(snd_emux_t **remu) | ||
38 | { | ||
39 | snd_emux_t *emu; | ||
40 | |||
41 | *remu = NULL; | ||
42 | emu = kcalloc(1, sizeof(*emu), GFP_KERNEL); | ||
43 | if (emu == NULL) | ||
44 | return -ENOMEM; | ||
45 | |||
46 | spin_lock_init(&emu->voice_lock); | ||
47 | init_MUTEX(&emu->register_mutex); | ||
48 | |||
49 | emu->client = -1; | ||
50 | #ifdef CONFIG_SND_SEQUENCER_OSS | ||
51 | emu->oss_synth = NULL; | ||
52 | #endif | ||
53 | emu->max_voices = 0; | ||
54 | emu->use_time = 0; | ||
55 | |||
56 | init_timer(&emu->tlist); | ||
57 | emu->tlist.function = snd_emux_timer_callback; | ||
58 | emu->tlist.data = (unsigned long)emu; | ||
59 | emu->timer_active = 0; | ||
60 | |||
61 | *remu = emu; | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | |||
66 | /* | ||
67 | */ | ||
68 | int snd_emux_register(snd_emux_t *emu, snd_card_t *card, int index, char *name) | ||
69 | { | ||
70 | int err; | ||
71 | snd_sf_callback_t sf_cb; | ||
72 | |||
73 | snd_assert(emu->hw != NULL, return -EINVAL); | ||
74 | snd_assert(emu->max_voices > 0, return -EINVAL); | ||
75 | snd_assert(card != NULL, return -EINVAL); | ||
76 | snd_assert(name != NULL, return -EINVAL); | ||
77 | |||
78 | emu->card = card; | ||
79 | emu->name = snd_kmalloc_strdup(name, GFP_KERNEL); | ||
80 | emu->voices = kcalloc(emu->max_voices, sizeof(snd_emux_voice_t), GFP_KERNEL); | ||
81 | if (emu->voices == NULL) | ||
82 | return -ENOMEM; | ||
83 | |||
84 | /* create soundfont list */ | ||
85 | memset(&sf_cb, 0, sizeof(sf_cb)); | ||
86 | sf_cb.private_data = emu; | ||
87 | sf_cb.sample_new = (snd_sf_sample_new_t)emu->ops.sample_new; | ||
88 | sf_cb.sample_free = (snd_sf_sample_free_t)emu->ops.sample_free; | ||
89 | sf_cb.sample_reset = (snd_sf_sample_reset_t)emu->ops.sample_reset; | ||
90 | emu->sflist = snd_sf_new(&sf_cb, emu->memhdr); | ||
91 | if (emu->sflist == NULL) | ||
92 | return -ENOMEM; | ||
93 | |||
94 | if ((err = snd_emux_init_hwdep(emu)) < 0) | ||
95 | return err; | ||
96 | |||
97 | snd_emux_init_voices(emu); | ||
98 | |||
99 | snd_emux_init_seq(emu, card, index); | ||
100 | #ifdef CONFIG_SND_SEQUENCER_OSS | ||
101 | snd_emux_init_seq_oss(emu); | ||
102 | #endif | ||
103 | snd_emux_init_virmidi(emu, card); | ||
104 | |||
105 | #ifdef CONFIG_PROC_FS | ||
106 | snd_emux_proc_init(emu, card, index); | ||
107 | #endif | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | |||
112 | /* | ||
113 | */ | ||
114 | int snd_emux_free(snd_emux_t *emu) | ||
115 | { | ||
116 | unsigned long flags; | ||
117 | |||
118 | if (! emu) | ||
119 | return -EINVAL; | ||
120 | |||
121 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
122 | if (emu->timer_active) | ||
123 | del_timer(&emu->tlist); | ||
124 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
125 | |||
126 | #ifdef CONFIG_PROC_FS | ||
127 | snd_emux_proc_free(emu); | ||
128 | #endif | ||
129 | snd_emux_delete_virmidi(emu); | ||
130 | #ifdef CONFIG_SND_SEQUENCER_OSS | ||
131 | snd_emux_detach_seq_oss(emu); | ||
132 | #endif | ||
133 | snd_emux_detach_seq(emu); | ||
134 | |||
135 | snd_emux_delete_hwdep(emu); | ||
136 | |||
137 | if (emu->sflist) | ||
138 | snd_sf_free(emu->sflist); | ||
139 | |||
140 | kfree(emu->voices); | ||
141 | kfree(emu->name); | ||
142 | kfree(emu); | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | |||
147 | EXPORT_SYMBOL(snd_emux_new); | ||
148 | EXPORT_SYMBOL(snd_emux_register); | ||
149 | EXPORT_SYMBOL(snd_emux_free); | ||
150 | |||
151 | EXPORT_SYMBOL(snd_emux_terminate_all); | ||
152 | EXPORT_SYMBOL(snd_emux_lock_voice); | ||
153 | EXPORT_SYMBOL(snd_emux_unlock_voice); | ||
154 | |||
155 | /* soundfont.c */ | ||
156 | EXPORT_SYMBOL(snd_sf_linear_to_log); | ||
157 | |||
158 | |||
159 | /* | ||
160 | * INIT part | ||
161 | */ | ||
162 | |||
163 | static int __init alsa_emux_init(void) | ||
164 | { | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static void __exit alsa_emux_exit(void) | ||
169 | { | ||
170 | } | ||
171 | |||
172 | module_init(alsa_emux_init) | ||
173 | module_exit(alsa_emux_exit) | ||
diff --git a/sound/synth/emux/emux_effect.c b/sound/synth/emux/emux_effect.c new file mode 100644 index 000000000000..ec3fc1ba7fca --- /dev/null +++ b/sound/synth/emux/emux_effect.c | |||
@@ -0,0 +1,308 @@ | |||
1 | /* | ||
2 | * Midi synth routines for the Emu8k/Emu10k1 | ||
3 | * | ||
4 | * Copyright (C) 1999 Steve Ratcliffe | ||
5 | * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
6 | * | ||
7 | * Contains code based on awe_wave.c by Takashi Iwai | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include "emux_voice.h" | ||
26 | #include <linux/slab.h> | ||
27 | |||
28 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
29 | /* | ||
30 | * effects table | ||
31 | */ | ||
32 | |||
33 | #define xoffsetof(type,tag) ((long)(&((type)NULL)->tag) - (long)(NULL)) | ||
34 | |||
35 | #define parm_offset(tag) xoffsetof(soundfont_voice_parm_t*, tag) | ||
36 | |||
37 | #define PARM_IS_BYTE (1 << 0) | ||
38 | #define PARM_IS_WORD (1 << 1) | ||
39 | #define PARM_IS_ALIGNED (3 << 2) | ||
40 | #define PARM_IS_ALIGN_HI (1 << 2) | ||
41 | #define PARM_IS_ALIGN_LO (2 << 2) | ||
42 | #define PARM_IS_SIGNED (1 << 4) | ||
43 | |||
44 | #define PARM_WORD (PARM_IS_WORD) | ||
45 | #define PARM_BYTE_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO) | ||
46 | #define PARM_BYTE_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI) | ||
47 | #define PARM_BYTE (PARM_IS_BYTE) | ||
48 | #define PARM_SIGN_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO|PARM_IS_SIGNED) | ||
49 | #define PARM_SIGN_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI|PARM_IS_SIGNED) | ||
50 | |||
51 | static struct emux_parm_defs { | ||
52 | int type; /* byte or word */ | ||
53 | int low, high; /* value range */ | ||
54 | long offset; /* offset in parameter record (-1 = not written) */ | ||
55 | int update; /* flgas for real-time update */ | ||
56 | } parm_defs[EMUX_NUM_EFFECTS] = { | ||
57 | {PARM_WORD, 0, 0x8000, parm_offset(moddelay), 0}, /* env1 delay */ | ||
58 | {PARM_BYTE_LO, 1, 0x80, parm_offset(modatkhld), 0}, /* env1 attack */ | ||
59 | {PARM_BYTE_HI, 0, 0x7e, parm_offset(modatkhld), 0}, /* env1 hold */ | ||
60 | {PARM_BYTE_LO, 1, 0x7f, parm_offset(moddcysus), 0}, /* env1 decay */ | ||
61 | {PARM_BYTE_LO, 1, 0x7f, parm_offset(modrelease), 0}, /* env1 release */ | ||
62 | {PARM_BYTE_HI, 0, 0x7f, parm_offset(moddcysus), 0}, /* env1 sustain */ | ||
63 | {PARM_BYTE_HI, 0, 0xff, parm_offset(pefe), 0}, /* env1 pitch */ | ||
64 | {PARM_BYTE_LO, 0, 0xff, parm_offset(pefe), 0}, /* env1 fc */ | ||
65 | |||
66 | {PARM_WORD, 0, 0x8000, parm_offset(voldelay), 0}, /* env2 delay */ | ||
67 | {PARM_BYTE_LO, 1, 0x80, parm_offset(volatkhld), 0}, /* env2 attack */ | ||
68 | {PARM_BYTE_HI, 0, 0x7e, parm_offset(volatkhld), 0}, /* env2 hold */ | ||
69 | {PARM_BYTE_LO, 1, 0x7f, parm_offset(voldcysus), 0}, /* env2 decay */ | ||
70 | {PARM_BYTE_LO, 1, 0x7f, parm_offset(volrelease), 0}, /* env2 release */ | ||
71 | {PARM_BYTE_HI, 0, 0x7f, parm_offset(voldcysus), 0}, /* env2 sustain */ | ||
72 | |||
73 | {PARM_WORD, 0, 0x8000, parm_offset(lfo1delay), 0}, /* lfo1 delay */ | ||
74 | {PARM_BYTE_LO, 0, 0xff, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 freq */ | ||
75 | {PARM_SIGN_HI, -128, 127, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 vol */ | ||
76 | {PARM_SIGN_HI, -128, 127, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 pitch */ | ||
77 | {PARM_BYTE_LO, 0, 0xff, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 cutoff */ | ||
78 | |||
79 | {PARM_WORD, 0, 0x8000, parm_offset(lfo2delay), 0}, /* lfo2 delay */ | ||
80 | {PARM_BYTE_LO, 0, 0xff, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 freq */ | ||
81 | {PARM_SIGN_HI, -128, 127, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 pitch */ | ||
82 | |||
83 | {PARM_WORD, 0, 0xffff, -1, SNDRV_EMUX_UPDATE_PITCH}, /* initial pitch */ | ||
84 | {PARM_BYTE, 0, 0xff, parm_offset(chorus), 0}, /* chorus */ | ||
85 | {PARM_BYTE, 0, 0xff, parm_offset(reverb), 0}, /* reverb */ | ||
86 | {PARM_BYTE, 0, 0xff, parm_offset(cutoff), SNDRV_EMUX_UPDATE_VOLUME}, /* cutoff */ | ||
87 | {PARM_BYTE, 0, 15, parm_offset(filterQ), SNDRV_EMUX_UPDATE_Q}, /* resonance */ | ||
88 | |||
89 | {PARM_WORD, 0, 0xffff, -1, 0}, /* sample start */ | ||
90 | {PARM_WORD, 0, 0xffff, -1, 0}, /* loop start */ | ||
91 | {PARM_WORD, 0, 0xffff, -1, 0}, /* loop end */ | ||
92 | {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse sample start */ | ||
93 | {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop start */ | ||
94 | {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop end */ | ||
95 | {PARM_BYTE, 0, 0xff, -1, SNDRV_EMUX_UPDATE_VOLUME}, /* initial attenuation */ | ||
96 | }; | ||
97 | |||
98 | /* set byte effect value */ | ||
99 | static void | ||
100 | effect_set_byte(unsigned char *valp, snd_midi_channel_t *chan, int type) | ||
101 | { | ||
102 | short effect; | ||
103 | snd_emux_effect_table_t *fx = chan->private; | ||
104 | |||
105 | effect = fx->val[type]; | ||
106 | if (fx->flag[type] == EMUX_FX_FLAG_ADD) { | ||
107 | if (parm_defs[type].type & PARM_IS_SIGNED) | ||
108 | effect += *(char*)valp; | ||
109 | else | ||
110 | effect += *valp; | ||
111 | } | ||
112 | if (effect < parm_defs[type].low) | ||
113 | effect = parm_defs[type].low; | ||
114 | else if (effect > parm_defs[type].high) | ||
115 | effect = parm_defs[type].high; | ||
116 | *valp = (unsigned char)effect; | ||
117 | } | ||
118 | |||
119 | /* set word effect value */ | ||
120 | static void | ||
121 | effect_set_word(unsigned short *valp, snd_midi_channel_t *chan, int type) | ||
122 | { | ||
123 | int effect; | ||
124 | snd_emux_effect_table_t *fx = chan->private; | ||
125 | |||
126 | effect = *(unsigned short*)&fx->val[type]; | ||
127 | if (fx->flag[type] == EMUX_FX_FLAG_ADD) | ||
128 | effect += *valp; | ||
129 | if (effect < parm_defs[type].low) | ||
130 | effect = parm_defs[type].low; | ||
131 | else if (effect > parm_defs[type].high) | ||
132 | effect = parm_defs[type].high; | ||
133 | *valp = (unsigned short)effect; | ||
134 | } | ||
135 | |||
136 | /* address offset */ | ||
137 | static int | ||
138 | effect_get_offset(snd_midi_channel_t *chan, int lo, int hi, int mode) | ||
139 | { | ||
140 | int addr = 0; | ||
141 | snd_emux_effect_table_t *fx = chan->private; | ||
142 | |||
143 | if (fx->flag[hi]) | ||
144 | addr = (short)fx->val[hi]; | ||
145 | addr = addr << 15; | ||
146 | if (fx->flag[lo]) | ||
147 | addr += (short)fx->val[lo]; | ||
148 | if (!(mode & SNDRV_SFNT_SAMPLE_8BITS)) | ||
149 | addr /= 2; | ||
150 | return addr; | ||
151 | } | ||
152 | |||
153 | #ifdef CONFIG_SND_SEQUENCER_OSS | ||
154 | /* change effects - for OSS sequencer compatibility */ | ||
155 | void | ||
156 | snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val) | ||
157 | { | ||
158 | int mode; | ||
159 | |||
160 | if (type & 0x40) | ||
161 | mode = EMUX_FX_FLAG_OFF; | ||
162 | else if (type & 0x80) | ||
163 | mode = EMUX_FX_FLAG_ADD; | ||
164 | else | ||
165 | mode = EMUX_FX_FLAG_SET; | ||
166 | type &= 0x3f; | ||
167 | |||
168 | snd_emux_send_effect(port, chan, type, val, mode); | ||
169 | } | ||
170 | #endif | ||
171 | |||
172 | /* Modify the effect value. | ||
173 | * if update is necessary, call emu8000_control | ||
174 | */ | ||
175 | void | ||
176 | snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode) | ||
177 | { | ||
178 | int i; | ||
179 | int offset; | ||
180 | unsigned char *srcp, *origp; | ||
181 | snd_emux_t *emu; | ||
182 | snd_emux_effect_table_t *fx; | ||
183 | unsigned long flags; | ||
184 | |||
185 | emu = port->emu; | ||
186 | fx = chan->private; | ||
187 | if (emu == NULL || fx == NULL) | ||
188 | return; | ||
189 | if (type < 0 || type >= EMUX_NUM_EFFECTS) | ||
190 | return; | ||
191 | |||
192 | fx->val[type] = val; | ||
193 | fx->flag[type] = mode; | ||
194 | |||
195 | /* do we need to modify the register in realtime ? */ | ||
196 | if (! parm_defs[type].update || (offset = parm_defs[type].offset) < 0) | ||
197 | return; | ||
198 | |||
199 | #ifdef SNDRV_LITTLE_ENDIAN | ||
200 | if (parm_defs[type].type & PARM_IS_ALIGN_HI) | ||
201 | offset++; | ||
202 | #else | ||
203 | if (parm_defs[type].type & PARM_IS_ALIGN_LO) | ||
204 | offset++; | ||
205 | #endif | ||
206 | /* modify the register values */ | ||
207 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
208 | for (i = 0; i < emu->max_voices; i++) { | ||
209 | snd_emux_voice_t *vp = &emu->voices[i]; | ||
210 | if (!STATE_IS_PLAYING(vp->state) || vp->chan != chan) | ||
211 | continue; | ||
212 | srcp = (unsigned char*)&vp->reg.parm + offset; | ||
213 | origp = (unsigned char*)&vp->zone->v.parm + offset; | ||
214 | if (parm_defs[i].type & PARM_IS_BYTE) { | ||
215 | *srcp = *origp; | ||
216 | effect_set_byte(srcp, chan, type); | ||
217 | } else { | ||
218 | *(unsigned short*)srcp = *(unsigned short*)origp; | ||
219 | effect_set_word((unsigned short*)srcp, chan, type); | ||
220 | } | ||
221 | } | ||
222 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
223 | |||
224 | /* activate them */ | ||
225 | snd_emux_update_channel(port, chan, parm_defs[type].update); | ||
226 | } | ||
227 | |||
228 | |||
229 | /* copy wavetable registers to voice table */ | ||
230 | void | ||
231 | snd_emux_setup_effect(snd_emux_voice_t *vp) | ||
232 | { | ||
233 | snd_midi_channel_t *chan = vp->chan; | ||
234 | snd_emux_effect_table_t *fx; | ||
235 | unsigned char *srcp; | ||
236 | int i; | ||
237 | |||
238 | if (! (fx = chan->private)) | ||
239 | return; | ||
240 | |||
241 | /* modify the register values via effect table */ | ||
242 | for (i = 0; i < EMUX_FX_END; i++) { | ||
243 | int offset; | ||
244 | if (! fx->flag[i] || (offset = parm_defs[i].offset) < 0) | ||
245 | continue; | ||
246 | #ifdef SNDRV_LITTLE_ENDIAN | ||
247 | if (parm_defs[i].type & PARM_IS_ALIGN_HI) | ||
248 | offset++; | ||
249 | #else | ||
250 | if (parm_defs[i].type & PARM_IS_ALIGN_LO) | ||
251 | offset++; | ||
252 | #endif | ||
253 | srcp = (unsigned char*)&vp->reg.parm + offset; | ||
254 | if (parm_defs[i].type & PARM_IS_BYTE) | ||
255 | effect_set_byte(srcp, chan, i); | ||
256 | else | ||
257 | effect_set_word((unsigned short*)srcp, chan, i); | ||
258 | } | ||
259 | |||
260 | /* correct sample and loop points */ | ||
261 | vp->reg.start += effect_get_offset(chan, EMUX_FX_SAMPLE_START, | ||
262 | EMUX_FX_COARSE_SAMPLE_START, | ||
263 | vp->reg.sample_mode); | ||
264 | |||
265 | vp->reg.loopstart += effect_get_offset(chan, EMUX_FX_LOOP_START, | ||
266 | EMUX_FX_COARSE_LOOP_START, | ||
267 | vp->reg.sample_mode); | ||
268 | |||
269 | vp->reg.loopend += effect_get_offset(chan, EMUX_FX_LOOP_END, | ||
270 | EMUX_FX_COARSE_LOOP_END, | ||
271 | vp->reg.sample_mode); | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * effect table | ||
276 | */ | ||
277 | void | ||
278 | snd_emux_create_effect(snd_emux_port_t *p) | ||
279 | { | ||
280 | int i; | ||
281 | p->effect = kcalloc(p->chset.max_channels, sizeof(snd_emux_effect_table_t), GFP_KERNEL); | ||
282 | if (p->effect) { | ||
283 | for (i = 0; i < p->chset.max_channels; i++) | ||
284 | p->chset.channels[i].private = p->effect + i; | ||
285 | } else { | ||
286 | for (i = 0; i < p->chset.max_channels; i++) | ||
287 | p->chset.channels[i].private = NULL; | ||
288 | } | ||
289 | } | ||
290 | |||
291 | void | ||
292 | snd_emux_delete_effect(snd_emux_port_t *p) | ||
293 | { | ||
294 | if (p->effect) { | ||
295 | kfree(p->effect); | ||
296 | p->effect = NULL; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | void | ||
301 | snd_emux_clear_effect(snd_emux_port_t *p) | ||
302 | { | ||
303 | if (p->effect) { | ||
304 | memset(p->effect, 0, sizeof(snd_emux_effect_table_t) * p->chset.max_channels); | ||
305 | } | ||
306 | } | ||
307 | |||
308 | #endif /* SNDRV_EMUX_USE_RAW_EFFECT */ | ||
diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c new file mode 100644 index 000000000000..4182b44eb47e --- /dev/null +++ b/sound/synth/emux/emux_hwdep.c | |||
@@ -0,0 +1,171 @@ | |||
1 | /* | ||
2 | * Interface for hwdep device | ||
3 | * | ||
4 | * Copyright (C) 2004 Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <sound/core.h> | ||
24 | #include <sound/hwdep.h> | ||
25 | #include <asm/uaccess.h> | ||
26 | #include "emux_voice.h" | ||
27 | |||
28 | /* | ||
29 | * open the hwdep device | ||
30 | */ | ||
31 | static int | ||
32 | snd_emux_hwdep_open(snd_hwdep_t *hw, struct file *file) | ||
33 | { | ||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | |||
38 | /* | ||
39 | * close the device | ||
40 | */ | ||
41 | static int | ||
42 | snd_emux_hwdep_release(snd_hwdep_t *hw, struct file *file) | ||
43 | { | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | |||
48 | #define TMP_CLIENT_ID 0x1001 | ||
49 | |||
50 | /* | ||
51 | * load patch | ||
52 | */ | ||
53 | static int | ||
54 | snd_emux_hwdep_load_patch(snd_emux_t *emu, void __user *arg) | ||
55 | { | ||
56 | int err; | ||
57 | soundfont_patch_info_t patch; | ||
58 | |||
59 | if (copy_from_user(&patch, arg, sizeof(patch))) | ||
60 | return -EFAULT; | ||
61 | |||
62 | if (patch.type >= SNDRV_SFNT_LOAD_INFO && | ||
63 | patch.type <= SNDRV_SFNT_PROBE_DATA) { | ||
64 | err = snd_soundfont_load(emu->sflist, arg, patch.len + sizeof(patch), TMP_CLIENT_ID); | ||
65 | if (err < 0) | ||
66 | return err; | ||
67 | } else { | ||
68 | if (emu->ops.load_fx) | ||
69 | return emu->ops.load_fx(emu, patch.type, patch.optarg, arg, patch.len + sizeof(patch)); | ||
70 | else | ||
71 | return -EINVAL; | ||
72 | } | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * set misc mode | ||
78 | */ | ||
79 | static int | ||
80 | snd_emux_hwdep_misc_mode(snd_emux_t *emu, void __user *arg) | ||
81 | { | ||
82 | struct sndrv_emux_misc_mode info; | ||
83 | int i; | ||
84 | |||
85 | if (copy_from_user(&info, arg, sizeof(info))) | ||
86 | return -EFAULT; | ||
87 | if (info.mode < 0 || info.mode >= EMUX_MD_END) | ||
88 | return -EINVAL; | ||
89 | |||
90 | if (info.port < 0) { | ||
91 | for (i = 0; i < emu->num_ports; i++) | ||
92 | emu->portptrs[i]->ctrls[info.mode] = info.value; | ||
93 | } else { | ||
94 | if (info.port < emu->num_ports) | ||
95 | emu->portptrs[info.port]->ctrls[info.mode] = info.value; | ||
96 | } | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | |||
101 | /* | ||
102 | * ioctl | ||
103 | */ | ||
104 | static int | ||
105 | snd_emux_hwdep_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) | ||
106 | { | ||
107 | snd_emux_t *emu = hw->private_data; | ||
108 | |||
109 | switch (cmd) { | ||
110 | case SNDRV_EMUX_IOCTL_VERSION: | ||
111 | return put_user(SNDRV_EMUX_VERSION, (unsigned int __user *)arg); | ||
112 | case SNDRV_EMUX_IOCTL_LOAD_PATCH: | ||
113 | return snd_emux_hwdep_load_patch(emu, (void __user *)arg); | ||
114 | case SNDRV_EMUX_IOCTL_RESET_SAMPLES: | ||
115 | snd_soundfont_remove_samples(emu->sflist); | ||
116 | break; | ||
117 | case SNDRV_EMUX_IOCTL_REMOVE_LAST_SAMPLES: | ||
118 | snd_soundfont_remove_unlocked(emu->sflist); | ||
119 | break; | ||
120 | case SNDRV_EMUX_IOCTL_MEM_AVAIL: | ||
121 | if (emu->memhdr) { | ||
122 | int size = snd_util_mem_avail(emu->memhdr); | ||
123 | return put_user(size, (unsigned int __user *)arg); | ||
124 | } | ||
125 | break; | ||
126 | case SNDRV_EMUX_IOCTL_MISC_MODE: | ||
127 | return snd_emux_hwdep_misc_mode(emu, (void __user *)arg); | ||
128 | } | ||
129 | |||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | |||
134 | /* | ||
135 | * register hwdep device | ||
136 | */ | ||
137 | |||
138 | int | ||
139 | snd_emux_init_hwdep(snd_emux_t *emu) | ||
140 | { | ||
141 | snd_hwdep_t *hw; | ||
142 | int err; | ||
143 | |||
144 | if ((err = snd_hwdep_new(emu->card, SNDRV_EMUX_HWDEP_NAME, emu->hwdep_idx, &hw)) < 0) | ||
145 | return err; | ||
146 | emu->hwdep = hw; | ||
147 | strcpy(hw->name, SNDRV_EMUX_HWDEP_NAME); | ||
148 | hw->iface = SNDRV_HWDEP_IFACE_EMUX_WAVETABLE; | ||
149 | hw->ops.open = snd_emux_hwdep_open; | ||
150 | hw->ops.release = snd_emux_hwdep_release; | ||
151 | hw->ops.ioctl = snd_emux_hwdep_ioctl; | ||
152 | hw->exclusive = 1; | ||
153 | hw->private_data = emu; | ||
154 | if ((err = snd_card_register(emu->card)) < 0) | ||
155 | return err; | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | |||
161 | /* | ||
162 | * unregister | ||
163 | */ | ||
164 | void | ||
165 | snd_emux_delete_hwdep(snd_emux_t *emu) | ||
166 | { | ||
167 | if (emu->hwdep) { | ||
168 | snd_device_free(emu->card, emu->hwdep); | ||
169 | emu->hwdep = NULL; | ||
170 | } | ||
171 | } | ||
diff --git a/sound/synth/emux/emux_nrpn.c b/sound/synth/emux/emux_nrpn.c new file mode 100644 index 000000000000..25edff9e1fc2 --- /dev/null +++ b/sound/synth/emux/emux_nrpn.c | |||
@@ -0,0 +1,392 @@ | |||
1 | /* | ||
2 | * NRPN / SYSEX callbacks for Emu8k/Emu10k1 | ||
3 | * | ||
4 | * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "emux_voice.h" | ||
23 | #include <sound/asoundef.h> | ||
24 | |||
25 | /* | ||
26 | * conversion from NRPN/control parameters to Emu8000 raw parameters | ||
27 | */ | ||
28 | |||
29 | /* NRPN / CC -> Emu8000 parameter converter */ | ||
30 | typedef struct { | ||
31 | int control; | ||
32 | int effect; | ||
33 | int (*convert)(int val); | ||
34 | } nrpn_conv_table; | ||
35 | |||
36 | /* effect sensitivity */ | ||
37 | |||
38 | #define FX_CUTOFF 0 | ||
39 | #define FX_RESONANCE 1 | ||
40 | #define FX_ATTACK 2 | ||
41 | #define FX_RELEASE 3 | ||
42 | #define FX_VIBRATE 4 | ||
43 | #define FX_VIBDEPTH 5 | ||
44 | #define FX_VIBDELAY 6 | ||
45 | #define FX_NUMS 7 | ||
46 | |||
47 | /* | ||
48 | * convert NRPN/control values | ||
49 | */ | ||
50 | |||
51 | static int send_converted_effect(nrpn_conv_table *table, int num_tables, | ||
52 | snd_emux_port_t *port, snd_midi_channel_t *chan, | ||
53 | int type, int val, int mode) | ||
54 | { | ||
55 | int i, cval; | ||
56 | for (i = 0; i < num_tables; i++) { | ||
57 | if (table[i].control == type) { | ||
58 | cval = table[i].convert(val); | ||
59 | snd_emux_send_effect(port, chan, table[i].effect, | ||
60 | cval, mode); | ||
61 | return 1; | ||
62 | } | ||
63 | } | ||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | #define DEF_FX_CUTOFF 170 | ||
68 | #define DEF_FX_RESONANCE 6 | ||
69 | #define DEF_FX_ATTACK 50 | ||
70 | #define DEF_FX_RELEASE 50 | ||
71 | #define DEF_FX_VIBRATE 30 | ||
72 | #define DEF_FX_VIBDEPTH 4 | ||
73 | #define DEF_FX_VIBDELAY 1500 | ||
74 | |||
75 | /* effect sensitivities for GS NRPN: | ||
76 | * adjusted for chaos 8MB soundfonts | ||
77 | */ | ||
78 | static int gs_sense[] = | ||
79 | { | ||
80 | DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, | ||
81 | DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY | ||
82 | }; | ||
83 | |||
84 | /* effect sensitivies for XG controls: | ||
85 | * adjusted for chaos 8MB soundfonts | ||
86 | */ | ||
87 | static int xg_sense[] = | ||
88 | { | ||
89 | DEF_FX_CUTOFF, DEF_FX_RESONANCE, DEF_FX_ATTACK, DEF_FX_RELEASE, | ||
90 | DEF_FX_VIBRATE, DEF_FX_VIBDEPTH, DEF_FX_VIBDELAY | ||
91 | }; | ||
92 | |||
93 | |||
94 | /* | ||
95 | * AWE32 NRPN effects | ||
96 | */ | ||
97 | |||
98 | static int fx_delay(int val); | ||
99 | static int fx_attack(int val); | ||
100 | static int fx_hold(int val); | ||
101 | static int fx_decay(int val); | ||
102 | static int fx_the_value(int val); | ||
103 | static int fx_twice_value(int val); | ||
104 | static int fx_conv_pitch(int val); | ||
105 | static int fx_conv_Q(int val); | ||
106 | |||
107 | /* function for each NRPN */ /* [range] units */ | ||
108 | #define fx_env1_delay fx_delay /* [0,5900] 4msec */ | ||
109 | #define fx_env1_attack fx_attack /* [0,5940] 1msec */ | ||
110 | #define fx_env1_hold fx_hold /* [0,8191] 1msec */ | ||
111 | #define fx_env1_decay fx_decay /* [0,5940] 4msec */ | ||
112 | #define fx_env1_release fx_decay /* [0,5940] 4msec */ | ||
113 | #define fx_env1_sustain fx_the_value /* [0,127] 0.75dB */ | ||
114 | #define fx_env1_pitch fx_the_value /* [-127,127] 9.375cents */ | ||
115 | #define fx_env1_cutoff fx_the_value /* [-127,127] 56.25cents */ | ||
116 | |||
117 | #define fx_env2_delay fx_delay /* [0,5900] 4msec */ | ||
118 | #define fx_env2_attack fx_attack /* [0,5940] 1msec */ | ||
119 | #define fx_env2_hold fx_hold /* [0,8191] 1msec */ | ||
120 | #define fx_env2_decay fx_decay /* [0,5940] 4msec */ | ||
121 | #define fx_env2_release fx_decay /* [0,5940] 4msec */ | ||
122 | #define fx_env2_sustain fx_the_value /* [0,127] 0.75dB */ | ||
123 | |||
124 | #define fx_lfo1_delay fx_delay /* [0,5900] 4msec */ | ||
125 | #define fx_lfo1_freq fx_twice_value /* [0,127] 84mHz */ | ||
126 | #define fx_lfo1_volume fx_twice_value /* [0,127] 0.1875dB */ | ||
127 | #define fx_lfo1_pitch fx_the_value /* [-127,127] 9.375cents */ | ||
128 | #define fx_lfo1_cutoff fx_twice_value /* [-64,63] 56.25cents */ | ||
129 | |||
130 | #define fx_lfo2_delay fx_delay /* [0,5900] 4msec */ | ||
131 | #define fx_lfo2_freq fx_twice_value /* [0,127] 84mHz */ | ||
132 | #define fx_lfo2_pitch fx_the_value /* [-127,127] 9.375cents */ | ||
133 | |||
134 | #define fx_init_pitch fx_conv_pitch /* [-8192,8192] cents */ | ||
135 | #define fx_chorus fx_the_value /* [0,255] -- */ | ||
136 | #define fx_reverb fx_the_value /* [0,255] -- */ | ||
137 | #define fx_cutoff fx_twice_value /* [0,127] 62Hz */ | ||
138 | #define fx_filterQ fx_conv_Q /* [0,127] -- */ | ||
139 | |||
140 | static int fx_delay(int val) | ||
141 | { | ||
142 | return (unsigned short)snd_sf_calc_parm_delay(val); | ||
143 | } | ||
144 | |||
145 | static int fx_attack(int val) | ||
146 | { | ||
147 | return (unsigned short)snd_sf_calc_parm_attack(val); | ||
148 | } | ||
149 | |||
150 | static int fx_hold(int val) | ||
151 | { | ||
152 | return (unsigned short)snd_sf_calc_parm_hold(val); | ||
153 | } | ||
154 | |||
155 | static int fx_decay(int val) | ||
156 | { | ||
157 | return (unsigned short)snd_sf_calc_parm_decay(val); | ||
158 | } | ||
159 | |||
160 | static int fx_the_value(int val) | ||
161 | { | ||
162 | return (unsigned short)(val & 0xff); | ||
163 | } | ||
164 | |||
165 | static int fx_twice_value(int val) | ||
166 | { | ||
167 | return (unsigned short)((val * 2) & 0xff); | ||
168 | } | ||
169 | |||
170 | static int fx_conv_pitch(int val) | ||
171 | { | ||
172 | return (short)(val * 4096 / 1200); | ||
173 | } | ||
174 | |||
175 | static int fx_conv_Q(int val) | ||
176 | { | ||
177 | return (unsigned short)((val / 8) & 0xff); | ||
178 | } | ||
179 | |||
180 | |||
181 | static nrpn_conv_table awe_effects[] = | ||
182 | { | ||
183 | { 0, EMUX_FX_LFO1_DELAY, fx_lfo1_delay}, | ||
184 | { 1, EMUX_FX_LFO1_FREQ, fx_lfo1_freq}, | ||
185 | { 2, EMUX_FX_LFO2_DELAY, fx_lfo2_delay}, | ||
186 | { 3, EMUX_FX_LFO2_FREQ, fx_lfo2_freq}, | ||
187 | |||
188 | { 4, EMUX_FX_ENV1_DELAY, fx_env1_delay}, | ||
189 | { 5, EMUX_FX_ENV1_ATTACK,fx_env1_attack}, | ||
190 | { 6, EMUX_FX_ENV1_HOLD, fx_env1_hold}, | ||
191 | { 7, EMUX_FX_ENV1_DECAY, fx_env1_decay}, | ||
192 | { 8, EMUX_FX_ENV1_SUSTAIN, fx_env1_sustain}, | ||
193 | { 9, EMUX_FX_ENV1_RELEASE, fx_env1_release}, | ||
194 | |||
195 | {10, EMUX_FX_ENV2_DELAY, fx_env2_delay}, | ||
196 | {11, EMUX_FX_ENV2_ATTACK, fx_env2_attack}, | ||
197 | {12, EMUX_FX_ENV2_HOLD, fx_env2_hold}, | ||
198 | {13, EMUX_FX_ENV2_DECAY, fx_env2_decay}, | ||
199 | {14, EMUX_FX_ENV2_SUSTAIN, fx_env2_sustain}, | ||
200 | {15, EMUX_FX_ENV2_RELEASE, fx_env2_release}, | ||
201 | |||
202 | {16, EMUX_FX_INIT_PITCH, fx_init_pitch}, | ||
203 | {17, EMUX_FX_LFO1_PITCH, fx_lfo1_pitch}, | ||
204 | {18, EMUX_FX_LFO2_PITCH, fx_lfo2_pitch}, | ||
205 | {19, EMUX_FX_ENV1_PITCH, fx_env1_pitch}, | ||
206 | {20, EMUX_FX_LFO1_VOLUME, fx_lfo1_volume}, | ||
207 | {21, EMUX_FX_CUTOFF, fx_cutoff}, | ||
208 | {22, EMUX_FX_FILTERQ, fx_filterQ}, | ||
209 | {23, EMUX_FX_LFO1_CUTOFF, fx_lfo1_cutoff}, | ||
210 | {24, EMUX_FX_ENV1_CUTOFF, fx_env1_cutoff}, | ||
211 | {25, EMUX_FX_CHORUS, fx_chorus}, | ||
212 | {26, EMUX_FX_REVERB, fx_reverb}, | ||
213 | }; | ||
214 | |||
215 | |||
216 | /* | ||
217 | * GS(SC88) NRPN effects; still experimental | ||
218 | */ | ||
219 | |||
220 | /* cutoff: quarter semitone step, max=255 */ | ||
221 | static int gs_cutoff(int val) | ||
222 | { | ||
223 | return (val - 64) * gs_sense[FX_CUTOFF] / 50; | ||
224 | } | ||
225 | |||
226 | /* resonance: 0 to 15(max) */ | ||
227 | static int gs_filterQ(int val) | ||
228 | { | ||
229 | return (val - 64) * gs_sense[FX_RESONANCE] / 50; | ||
230 | } | ||
231 | |||
232 | /* attack: */ | ||
233 | static int gs_attack(int val) | ||
234 | { | ||
235 | return -(val - 64) * gs_sense[FX_ATTACK] / 50; | ||
236 | } | ||
237 | |||
238 | /* decay: */ | ||
239 | static int gs_decay(int val) | ||
240 | { | ||
241 | return -(val - 64) * gs_sense[FX_RELEASE] / 50; | ||
242 | } | ||
243 | |||
244 | /* release: */ | ||
245 | static int gs_release(int val) | ||
246 | { | ||
247 | return -(val - 64) * gs_sense[FX_RELEASE] / 50; | ||
248 | } | ||
249 | |||
250 | /* vibrato freq: 0.042Hz step, max=255 */ | ||
251 | static int gs_vib_rate(int val) | ||
252 | { | ||
253 | return (val - 64) * gs_sense[FX_VIBRATE] / 50; | ||
254 | } | ||
255 | |||
256 | /* vibrato depth: max=127, 1 octave */ | ||
257 | static int gs_vib_depth(int val) | ||
258 | { | ||
259 | return (val - 64) * gs_sense[FX_VIBDEPTH] / 50; | ||
260 | } | ||
261 | |||
262 | /* vibrato delay: -0.725msec step */ | ||
263 | static int gs_vib_delay(int val) | ||
264 | { | ||
265 | return -(val - 64) * gs_sense[FX_VIBDELAY] / 50; | ||
266 | } | ||
267 | |||
268 | static nrpn_conv_table gs_effects[] = | ||
269 | { | ||
270 | {32, EMUX_FX_CUTOFF, gs_cutoff}, | ||
271 | {33, EMUX_FX_FILTERQ, gs_filterQ}, | ||
272 | {99, EMUX_FX_ENV2_ATTACK, gs_attack}, | ||
273 | {100, EMUX_FX_ENV2_DECAY, gs_decay}, | ||
274 | {102, EMUX_FX_ENV2_RELEASE, gs_release}, | ||
275 | {8, EMUX_FX_LFO1_FREQ, gs_vib_rate}, | ||
276 | {9, EMUX_FX_LFO1_VOLUME, gs_vib_depth}, | ||
277 | {10, EMUX_FX_LFO1_DELAY, gs_vib_delay}, | ||
278 | }; | ||
279 | |||
280 | |||
281 | /* | ||
282 | * NRPN events | ||
283 | */ | ||
284 | void | ||
285 | snd_emux_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset) | ||
286 | { | ||
287 | snd_emux_port_t *port; | ||
288 | |||
289 | port = p; | ||
290 | snd_assert(port != NULL, return); | ||
291 | snd_assert(chan != NULL, return); | ||
292 | |||
293 | if (chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 127 && | ||
294 | chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] <= 26) { | ||
295 | int val; | ||
296 | /* Win/DOS AWE32 specific NRPNs */ | ||
297 | /* both MSB/LSB necessary */ | ||
298 | val = (chan->control[MIDI_CTL_MSB_DATA_ENTRY] << 7) | | ||
299 | chan->control[MIDI_CTL_LSB_DATA_ENTRY]; | ||
300 | val -= 8192; | ||
301 | send_converted_effect | ||
302 | (awe_effects, ARRAY_SIZE(awe_effects), | ||
303 | port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB], | ||
304 | val, EMUX_FX_FLAG_SET); | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | if (port->chset.midi_mode == SNDRV_MIDI_MODE_GS && | ||
309 | chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 1) { | ||
310 | int val; | ||
311 | /* GS specific NRPNs */ | ||
312 | /* only MSB is valid */ | ||
313 | val = chan->control[MIDI_CTL_MSB_DATA_ENTRY]; | ||
314 | send_converted_effect | ||
315 | (gs_effects, ARRAY_SIZE(gs_effects), | ||
316 | port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB], | ||
317 | val, EMUX_FX_FLAG_ADD); | ||
318 | return; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | |||
323 | /* | ||
324 | * XG control effects; still experimental | ||
325 | */ | ||
326 | |||
327 | /* cutoff: quarter semitone step, max=255 */ | ||
328 | static int xg_cutoff(int val) | ||
329 | { | ||
330 | return (val - 64) * xg_sense[FX_CUTOFF] / 64; | ||
331 | } | ||
332 | |||
333 | /* resonance: 0(open) to 15(most nasal) */ | ||
334 | static int xg_filterQ(int val) | ||
335 | { | ||
336 | return (val - 64) * xg_sense[FX_RESONANCE] / 64; | ||
337 | } | ||
338 | |||
339 | /* attack: */ | ||
340 | static int xg_attack(int val) | ||
341 | { | ||
342 | return -(val - 64) * xg_sense[FX_ATTACK] / 64; | ||
343 | } | ||
344 | |||
345 | /* release: */ | ||
346 | static int xg_release(int val) | ||
347 | { | ||
348 | return -(val - 64) * xg_sense[FX_RELEASE] / 64; | ||
349 | } | ||
350 | |||
351 | static nrpn_conv_table xg_effects[] = | ||
352 | { | ||
353 | {71, EMUX_FX_CUTOFF, xg_cutoff}, | ||
354 | {74, EMUX_FX_FILTERQ, xg_filterQ}, | ||
355 | {72, EMUX_FX_ENV2_RELEASE, xg_release}, | ||
356 | {73, EMUX_FX_ENV2_ATTACK, xg_attack}, | ||
357 | }; | ||
358 | |||
359 | int | ||
360 | snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param) | ||
361 | { | ||
362 | return send_converted_effect(xg_effects, ARRAY_SIZE(xg_effects), | ||
363 | port, chan, param, | ||
364 | chan->control[param], | ||
365 | EMUX_FX_FLAG_ADD); | ||
366 | } | ||
367 | |||
368 | /* | ||
369 | * receive sysex | ||
370 | */ | ||
371 | void | ||
372 | snd_emux_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset) | ||
373 | { | ||
374 | snd_emux_port_t *port; | ||
375 | snd_emux_t *emu; | ||
376 | |||
377 | port = p; | ||
378 | snd_assert(port != NULL, return); | ||
379 | snd_assert(chset != NULL, return); | ||
380 | emu = port->emu; | ||
381 | |||
382 | switch (parsed) { | ||
383 | case SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME: | ||
384 | snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME); | ||
385 | break; | ||
386 | default: | ||
387 | if (emu->ops.sysex) | ||
388 | emu->ops.sysex(emu, buf, len, parsed, chset); | ||
389 | break; | ||
390 | } | ||
391 | } | ||
392 | |||
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c new file mode 100644 index 000000000000..5272f4ac8afd --- /dev/null +++ b/sound/synth/emux/emux_oss.c | |||
@@ -0,0 +1,497 @@ | |||
1 | /* | ||
2 | * Interface for OSS sequencer emulation | ||
3 | * | ||
4 | * Copyright (C) 1999 Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | * Changes | ||
21 | * 19990227 Steve Ratcliffe Made separate file and merged in latest | ||
22 | * midi emulation. | ||
23 | */ | ||
24 | |||
25 | #include <sound/driver.h> | ||
26 | |||
27 | #ifdef CONFIG_SND_SEQUENCER_OSS | ||
28 | |||
29 | #include <asm/uaccess.h> | ||
30 | #include <sound/core.h> | ||
31 | #include "emux_voice.h" | ||
32 | #include <sound/asoundef.h> | ||
33 | |||
34 | static int snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure); | ||
35 | static int snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg); | ||
36 | static int snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg); | ||
37 | static int snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, const char __user *buf, int offs, int count); | ||
38 | static int snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg); | ||
39 | static int snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); | ||
40 | static void reset_port_mode(snd_emux_port_t *port, int midi_mode); | ||
41 | static void emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop); | ||
42 | static void gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, unsigned char *event, int atomic, int hop); | ||
43 | static void fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop); | ||
44 | |||
45 | /* operators */ | ||
46 | static snd_seq_oss_callback_t oss_callback = { | ||
47 | .owner = THIS_MODULE, | ||
48 | .open = snd_emux_open_seq_oss, | ||
49 | .close = snd_emux_close_seq_oss, | ||
50 | .ioctl = snd_emux_ioctl_seq_oss, | ||
51 | .load_patch = snd_emux_load_patch_seq_oss, | ||
52 | .reset = snd_emux_reset_seq_oss, | ||
53 | }; | ||
54 | |||
55 | |||
56 | /* | ||
57 | * register OSS synth | ||
58 | */ | ||
59 | |||
60 | void | ||
61 | snd_emux_init_seq_oss(snd_emux_t *emu) | ||
62 | { | ||
63 | snd_seq_oss_reg_t *arg; | ||
64 | snd_seq_device_t *dev; | ||
65 | |||
66 | if (snd_seq_device_new(emu->card, 0, SNDRV_SEQ_DEV_ID_OSS, | ||
67 | sizeof(snd_seq_oss_reg_t), &dev) < 0) | ||
68 | return; | ||
69 | |||
70 | emu->oss_synth = dev; | ||
71 | strcpy(dev->name, emu->name); | ||
72 | arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); | ||
73 | arg->type = SYNTH_TYPE_SAMPLE; | ||
74 | arg->subtype = SAMPLE_TYPE_AWE32; | ||
75 | arg->nvoices = emu->max_voices; | ||
76 | arg->oper = oss_callback; | ||
77 | arg->private_data = emu; | ||
78 | |||
79 | /* register to OSS synth table */ | ||
80 | snd_device_register(emu->card, dev); | ||
81 | } | ||
82 | |||
83 | |||
84 | /* | ||
85 | * unregister | ||
86 | */ | ||
87 | void | ||
88 | snd_emux_detach_seq_oss(snd_emux_t *emu) | ||
89 | { | ||
90 | if (emu->oss_synth) { | ||
91 | snd_device_free(emu->card, emu->oss_synth); | ||
92 | emu->oss_synth = NULL; | ||
93 | } | ||
94 | } | ||
95 | |||
96 | |||
97 | /* use port number as a unique soundfont client number */ | ||
98 | #define SF_CLIENT_NO(p) ((p) + 0x1000) | ||
99 | |||
100 | /* | ||
101 | * open port for OSS sequencer | ||
102 | */ | ||
103 | static int | ||
104 | snd_emux_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure) | ||
105 | { | ||
106 | snd_emux_t *emu; | ||
107 | snd_emux_port_t *p; | ||
108 | snd_seq_port_callback_t callback; | ||
109 | char tmpname[64]; | ||
110 | |||
111 | emu = closure; | ||
112 | snd_assert(arg != NULL && emu != NULL, return -ENXIO); | ||
113 | |||
114 | down(&emu->register_mutex); | ||
115 | |||
116 | if (!snd_emux_inc_count(emu)) { | ||
117 | up(&emu->register_mutex); | ||
118 | return -EFAULT; | ||
119 | } | ||
120 | |||
121 | memset(&callback, 0, sizeof(callback)); | ||
122 | callback.owner = THIS_MODULE; | ||
123 | callback.event_input = snd_emux_event_oss_input; | ||
124 | |||
125 | sprintf(tmpname, "%s OSS Port", emu->name); | ||
126 | p = snd_emux_create_port(emu, tmpname, 32, | ||
127 | 1, &callback); | ||
128 | if (p == NULL) { | ||
129 | snd_printk("can't create port\n"); | ||
130 | snd_emux_dec_count(emu); | ||
131 | up(&emu->register_mutex); | ||
132 | return -ENOMEM; | ||
133 | } | ||
134 | |||
135 | /* fill the argument data */ | ||
136 | arg->private_data = p; | ||
137 | arg->addr.client = p->chset.client; | ||
138 | arg->addr.port = p->chset.port; | ||
139 | p->oss_arg = arg; | ||
140 | |||
141 | reset_port_mode(p, arg->seq_mode); | ||
142 | |||
143 | snd_emux_reset_port(p); | ||
144 | |||
145 | up(&emu->register_mutex); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | |||
150 | #define DEFAULT_DRUM_FLAGS ((1<<9) | (1<<25)) | ||
151 | |||
152 | /* | ||
153 | * reset port mode | ||
154 | */ | ||
155 | static void | ||
156 | reset_port_mode(snd_emux_port_t *port, int midi_mode) | ||
157 | { | ||
158 | if (midi_mode) { | ||
159 | port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_MIDI; | ||
160 | port->drum_flags = DEFAULT_DRUM_FLAGS; | ||
161 | port->volume_atten = 0; | ||
162 | port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_KEYPRESS; | ||
163 | } else { | ||
164 | port->port_mode = SNDRV_EMUX_PORT_MODE_OSS_SYNTH; | ||
165 | port->drum_flags = 0; | ||
166 | port->volume_atten = 32; | ||
167 | port->oss_arg->event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | |||
172 | /* | ||
173 | * close port | ||
174 | */ | ||
175 | static int | ||
176 | snd_emux_close_seq_oss(snd_seq_oss_arg_t *arg) | ||
177 | { | ||
178 | snd_emux_t *emu; | ||
179 | snd_emux_port_t *p; | ||
180 | |||
181 | snd_assert(arg != NULL, return -ENXIO); | ||
182 | p = arg->private_data; | ||
183 | snd_assert(p != NULL, return -ENXIO); | ||
184 | |||
185 | emu = p->emu; | ||
186 | snd_assert(emu != NULL, return -ENXIO); | ||
187 | |||
188 | down(&emu->register_mutex); | ||
189 | snd_emux_sounds_off_all(p); | ||
190 | snd_soundfont_close_check(emu->sflist, SF_CLIENT_NO(p->chset.port)); | ||
191 | snd_seq_event_port_detach(p->chset.client, p->chset.port); | ||
192 | snd_emux_dec_count(emu); | ||
193 | |||
194 | up(&emu->register_mutex); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | |||
199 | /* | ||
200 | * load patch | ||
201 | */ | ||
202 | static int | ||
203 | snd_emux_load_patch_seq_oss(snd_seq_oss_arg_t *arg, int format, | ||
204 | const char __user *buf, int offs, int count) | ||
205 | { | ||
206 | snd_emux_t *emu; | ||
207 | snd_emux_port_t *p; | ||
208 | int rc; | ||
209 | |||
210 | snd_assert(arg != NULL, return -ENXIO); | ||
211 | p = arg->private_data; | ||
212 | snd_assert(p != NULL, return -ENXIO); | ||
213 | |||
214 | emu = p->emu; | ||
215 | snd_assert(emu != NULL, return -ENXIO); | ||
216 | |||
217 | if (format == GUS_PATCH) | ||
218 | rc = snd_soundfont_load_guspatch(emu->sflist, buf, count, | ||
219 | SF_CLIENT_NO(p->chset.port)); | ||
220 | else if (format == SNDRV_OSS_SOUNDFONT_PATCH) { | ||
221 | soundfont_patch_info_t patch; | ||
222 | if (count < (int)sizeof(patch)) | ||
223 | rc = -EINVAL; | ||
224 | if (copy_from_user(&patch, buf, sizeof(patch))) | ||
225 | rc = -EFAULT; | ||
226 | if (patch.type >= SNDRV_SFNT_LOAD_INFO && | ||
227 | patch.type <= SNDRV_SFNT_PROBE_DATA) | ||
228 | rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port)); | ||
229 | else { | ||
230 | if (emu->ops.load_fx) | ||
231 | rc = emu->ops.load_fx(emu, patch.type, patch.optarg, buf, count); | ||
232 | else | ||
233 | rc = -EINVAL; | ||
234 | } | ||
235 | } else | ||
236 | rc = 0; | ||
237 | return rc; | ||
238 | } | ||
239 | |||
240 | |||
241 | /* | ||
242 | * ioctl | ||
243 | */ | ||
244 | static int | ||
245 | snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t *arg, unsigned int cmd, unsigned long ioarg) | ||
246 | { | ||
247 | snd_emux_port_t *p; | ||
248 | snd_emux_t *emu; | ||
249 | |||
250 | snd_assert(arg != NULL, return -ENXIO); | ||
251 | p = arg->private_data; | ||
252 | snd_assert(p != NULL, return -ENXIO); | ||
253 | |||
254 | emu = p->emu; | ||
255 | snd_assert(emu != NULL, return -ENXIO); | ||
256 | |||
257 | switch (cmd) { | ||
258 | case SNDCTL_SEQ_RESETSAMPLES: | ||
259 | snd_soundfont_remove_samples(emu->sflist); | ||
260 | return 0; | ||
261 | |||
262 | case SNDCTL_SYNTH_MEMAVL: | ||
263 | if (emu->memhdr) | ||
264 | return snd_util_mem_avail(emu->memhdr); | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | |||
272 | /* | ||
273 | * reset device | ||
274 | */ | ||
275 | static int | ||
276 | snd_emux_reset_seq_oss(snd_seq_oss_arg_t *arg) | ||
277 | { | ||
278 | snd_emux_port_t *p; | ||
279 | |||
280 | snd_assert(arg != NULL, return -ENXIO); | ||
281 | p = arg->private_data; | ||
282 | snd_assert(p != NULL, return -ENXIO); | ||
283 | snd_emux_reset_port(p); | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | |||
288 | /* | ||
289 | * receive raw events: only SEQ_PRIVATE is accepted. | ||
290 | */ | ||
291 | static int | ||
292 | snd_emux_event_oss_input(snd_seq_event_t *ev, int direct, void *private_data, | ||
293 | int atomic, int hop) | ||
294 | { | ||
295 | snd_emux_t *emu; | ||
296 | snd_emux_port_t *p; | ||
297 | unsigned char cmd, *data; | ||
298 | |||
299 | p = private_data; | ||
300 | snd_assert(p != NULL, return -EINVAL); | ||
301 | emu = p->emu; | ||
302 | snd_assert(emu != NULL, return -EINVAL); | ||
303 | if (ev->type != SNDRV_SEQ_EVENT_OSS) | ||
304 | return snd_emux_event_input(ev, direct, private_data, atomic, hop); | ||
305 | |||
306 | data = ev->data.raw8.d; | ||
307 | /* only SEQ_PRIVATE is accepted */ | ||
308 | if (data[0] != 0xfe) | ||
309 | return 0; | ||
310 | cmd = data[2] & _EMUX_OSS_MODE_VALUE_MASK; | ||
311 | if (data[2] & _EMUX_OSS_MODE_FLAG) | ||
312 | emuspec_control(emu, p, cmd, data, atomic, hop); | ||
313 | else | ||
314 | gusspec_control(emu, p, cmd, data, atomic, hop); | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | |||
319 | /* | ||
320 | * OSS/AWE driver specific h/w controls | ||
321 | */ | ||
322 | static void | ||
323 | emuspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, | ||
324 | unsigned char *event, int atomic, int hop) | ||
325 | { | ||
326 | int voice; | ||
327 | unsigned short p1; | ||
328 | short p2; | ||
329 | int i; | ||
330 | snd_midi_channel_t *chan; | ||
331 | |||
332 | voice = event[3]; | ||
333 | if (voice < 0 || voice >= port->chset.max_channels) | ||
334 | chan = NULL; | ||
335 | else | ||
336 | chan = &port->chset.channels[voice]; | ||
337 | |||
338 | p1 = *(unsigned short *) &event[4]; | ||
339 | p2 = *(short *) &event[6]; | ||
340 | |||
341 | switch (cmd) { | ||
342 | #if 0 /* don't do this atomically */ | ||
343 | case _EMUX_OSS_REMOVE_LAST_SAMPLES: | ||
344 | snd_soundfont_remove_unlocked(emu->sflist); | ||
345 | break; | ||
346 | #endif | ||
347 | case _EMUX_OSS_SEND_EFFECT: | ||
348 | if (chan) | ||
349 | snd_emux_send_effect_oss(port, chan, p1, p2); | ||
350 | break; | ||
351 | |||
352 | case _EMUX_OSS_TERMINATE_ALL: | ||
353 | snd_emux_terminate_all(emu); | ||
354 | break; | ||
355 | |||
356 | case _EMUX_OSS_TERMINATE_CHANNEL: | ||
357 | /*snd_emux_mute_channel(emu, chan);*/ | ||
358 | break; | ||
359 | case _EMUX_OSS_RESET_CHANNEL: | ||
360 | /*snd_emux_channel_init(chset, chan);*/ | ||
361 | break; | ||
362 | |||
363 | case _EMUX_OSS_RELEASE_ALL: | ||
364 | fake_event(emu, port, voice, MIDI_CTL_ALL_NOTES_OFF, 0, atomic, hop); | ||
365 | break; | ||
366 | case _EMUX_OSS_NOTEOFF_ALL: | ||
367 | fake_event(emu, port, voice, MIDI_CTL_ALL_SOUNDS_OFF, 0, atomic, hop); | ||
368 | break; | ||
369 | |||
370 | case _EMUX_OSS_INITIAL_VOLUME: | ||
371 | if (p2) { | ||
372 | port->volume_atten = (short)p1; | ||
373 | snd_emux_update_port(port, SNDRV_EMUX_UPDATE_VOLUME); | ||
374 | } | ||
375 | break; | ||
376 | |||
377 | case _EMUX_OSS_CHN_PRESSURE: | ||
378 | if (chan) { | ||
379 | chan->midi_pressure = p1; | ||
380 | snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_FMMOD|SNDRV_EMUX_UPDATE_FM2FRQ2); | ||
381 | } | ||
382 | break; | ||
383 | |||
384 | case _EMUX_OSS_CHANNEL_MODE: | ||
385 | reset_port_mode(port, p1); | ||
386 | snd_emux_reset_port(port); | ||
387 | break; | ||
388 | |||
389 | case _EMUX_OSS_DRUM_CHANNELS: | ||
390 | port->drum_flags = *(unsigned int*)&event[4]; | ||
391 | for (i = 0; i < port->chset.max_channels; i++) { | ||
392 | chan = &port->chset.channels[i]; | ||
393 | chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; | ||
394 | } | ||
395 | break; | ||
396 | |||
397 | case _EMUX_OSS_MISC_MODE: | ||
398 | if (p1 < EMUX_MD_END) | ||
399 | port->ctrls[p1] = p2; | ||
400 | break; | ||
401 | case _EMUX_OSS_DEBUG_MODE: | ||
402 | break; | ||
403 | |||
404 | default: | ||
405 | if (emu->ops.oss_ioctl) | ||
406 | emu->ops.oss_ioctl(emu, cmd, p1, p2); | ||
407 | break; | ||
408 | } | ||
409 | } | ||
410 | |||
411 | /* | ||
412 | * GUS specific h/w controls | ||
413 | */ | ||
414 | |||
415 | #include <linux/ultrasound.h> | ||
416 | |||
417 | static void | ||
418 | gusspec_control(snd_emux_t *emu, snd_emux_port_t *port, int cmd, | ||
419 | unsigned char *event, int atomic, int hop) | ||
420 | { | ||
421 | int voice; | ||
422 | unsigned short p1; | ||
423 | short p2; | ||
424 | int plong; | ||
425 | snd_midi_channel_t *chan; | ||
426 | |||
427 | if (port->port_mode != SNDRV_EMUX_PORT_MODE_OSS_SYNTH) | ||
428 | return; | ||
429 | if (cmd == _GUS_NUMVOICES) | ||
430 | return; | ||
431 | voice = event[3]; | ||
432 | if (voice < 0 || voice >= port->chset.max_channels) | ||
433 | return; | ||
434 | |||
435 | chan = &port->chset.channels[voice]; | ||
436 | |||
437 | p1 = *(unsigned short *) &event[4]; | ||
438 | p2 = *(short *) &event[6]; | ||
439 | plong = *(int*) &event[4]; | ||
440 | |||
441 | switch (cmd) { | ||
442 | case _GUS_VOICESAMPLE: | ||
443 | chan->midi_program = p1; | ||
444 | return; | ||
445 | |||
446 | case _GUS_VOICEBALA: | ||
447 | /* 0 to 15 --> 0 to 127 */ | ||
448 | chan->control[MIDI_CTL_MSB_PAN] = (int)p1 << 3; | ||
449 | snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN); | ||
450 | return; | ||
451 | |||
452 | case _GUS_VOICEVOL: | ||
453 | case _GUS_VOICEVOL2: | ||
454 | /* not supported yet */ | ||
455 | return; | ||
456 | |||
457 | case _GUS_RAMPRANGE: | ||
458 | case _GUS_RAMPRATE: | ||
459 | case _GUS_RAMPMODE: | ||
460 | case _GUS_RAMPON: | ||
461 | case _GUS_RAMPOFF: | ||
462 | /* volume ramping not supported */ | ||
463 | return; | ||
464 | |||
465 | case _GUS_VOLUME_SCALE: | ||
466 | return; | ||
467 | |||
468 | case _GUS_VOICE_POS: | ||
469 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
470 | snd_emux_send_effect(port, chan, EMUX_FX_SAMPLE_START, | ||
471 | (short)(plong & 0x7fff), | ||
472 | EMUX_FX_FLAG_SET); | ||
473 | snd_emux_send_effect(port, chan, EMUX_FX_COARSE_SAMPLE_START, | ||
474 | (plong >> 15) & 0xffff, | ||
475 | EMUX_FX_FLAG_SET); | ||
476 | #endif | ||
477 | return; | ||
478 | } | ||
479 | } | ||
480 | |||
481 | |||
482 | /* | ||
483 | * send an event to midi emulation | ||
484 | */ | ||
485 | static void | ||
486 | fake_event(snd_emux_t *emu, snd_emux_port_t *port, int ch, int param, int val, int atomic, int hop) | ||
487 | { | ||
488 | snd_seq_event_t ev; | ||
489 | memset(&ev, 0, sizeof(ev)); | ||
490 | ev.type = SNDRV_SEQ_EVENT_CONTROLLER; | ||
491 | ev.data.control.channel = ch; | ||
492 | ev.data.control.param = param; | ||
493 | ev.data.control.value = val; | ||
494 | snd_emux_event_input(&ev, 0, port, atomic, hop); | ||
495 | } | ||
496 | |||
497 | #endif /* CONFIG_SND_SEQUENCER_OSS */ | ||
diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c new file mode 100644 index 000000000000..0f155d68de0d --- /dev/null +++ b/sound/synth/emux/emux_proc.c | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de> | ||
3 | * | ||
4 | * Proc interface for Emu8k/Emu10k1 WaveTable synth | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/wait.h> | ||
23 | #include <linux/sched.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/emux_synth.h> | ||
27 | #include <sound/info.h> | ||
28 | #include "emux_voice.h" | ||
29 | |||
30 | #ifdef CONFIG_PROC_FS | ||
31 | |||
32 | static void | ||
33 | snd_emux_proc_info_read(snd_info_entry_t *entry, | ||
34 | snd_info_buffer_t *buf) | ||
35 | { | ||
36 | snd_emux_t *emu; | ||
37 | int i; | ||
38 | |||
39 | emu = entry->private_data; | ||
40 | down(&emu->register_mutex); | ||
41 | if (emu->name) | ||
42 | snd_iprintf(buf, "Device: %s\n", emu->name); | ||
43 | snd_iprintf(buf, "Ports: %d\n", emu->num_ports); | ||
44 | snd_iprintf(buf, "Addresses:"); | ||
45 | for (i = 0; i < emu->num_ports; i++) | ||
46 | snd_iprintf(buf, " %d:%d", emu->client, emu->ports[i]); | ||
47 | snd_iprintf(buf, "\n"); | ||
48 | snd_iprintf(buf, "Use Counter: %d\n", emu->used); | ||
49 | snd_iprintf(buf, "Max Voices: %d\n", emu->max_voices); | ||
50 | snd_iprintf(buf, "Allocated Voices: %d\n", emu->num_voices); | ||
51 | if (emu->memhdr) { | ||
52 | snd_iprintf(buf, "Memory Size: %d\n", emu->memhdr->size); | ||
53 | snd_iprintf(buf, "Memory Available: %d\n", snd_util_mem_avail(emu->memhdr)); | ||
54 | snd_iprintf(buf, "Allocated Blocks: %d\n", emu->memhdr->nblocks); | ||
55 | } else { | ||
56 | snd_iprintf(buf, "Memory Size: 0\n"); | ||
57 | } | ||
58 | if (emu->sflist) { | ||
59 | down(&emu->sflist->presets_mutex); | ||
60 | snd_iprintf(buf, "SoundFonts: %d\n", emu->sflist->fonts_size); | ||
61 | snd_iprintf(buf, "Instruments: %d\n", emu->sflist->zone_counter); | ||
62 | snd_iprintf(buf, "Samples: %d\n", emu->sflist->sample_counter); | ||
63 | snd_iprintf(buf, "Locked Instruments: %d\n", emu->sflist->zone_locked); | ||
64 | snd_iprintf(buf, "Locked Samples: %d\n", emu->sflist->sample_locked); | ||
65 | up(&emu->sflist->presets_mutex); | ||
66 | } | ||
67 | #if 0 /* debug */ | ||
68 | if (emu->voices[0].state != SNDRV_EMUX_ST_OFF && emu->voices[0].ch >= 0) { | ||
69 | snd_emux_voice_t *vp = &emu->voices[0]; | ||
70 | snd_iprintf(buf, "voice 0: on\n"); | ||
71 | snd_iprintf(buf, "mod delay=%x, atkhld=%x, dcysus=%x, rel=%x\n", | ||
72 | vp->reg.parm.moddelay, | ||
73 | vp->reg.parm.modatkhld, | ||
74 | vp->reg.parm.moddcysus, | ||
75 | vp->reg.parm.modrelease); | ||
76 | snd_iprintf(buf, "vol delay=%x, atkhld=%x, dcysus=%x, rel=%x\n", | ||
77 | vp->reg.parm.voldelay, | ||
78 | vp->reg.parm.volatkhld, | ||
79 | vp->reg.parm.voldcysus, | ||
80 | vp->reg.parm.volrelease); | ||
81 | snd_iprintf(buf, "lfo1 delay=%x, lfo2 delay=%x, pefe=%x\n", | ||
82 | vp->reg.parm.lfo1delay, | ||
83 | vp->reg.parm.lfo2delay, | ||
84 | vp->reg.parm.pefe); | ||
85 | snd_iprintf(buf, "fmmod=%x, tremfrq=%x, fm2frq2=%x\n", | ||
86 | vp->reg.parm.fmmod, | ||
87 | vp->reg.parm.tremfrq, | ||
88 | vp->reg.parm.fm2frq2); | ||
89 | snd_iprintf(buf, "cutoff=%x, filterQ=%x, chorus=%x, reverb=%x\n", | ||
90 | vp->reg.parm.cutoff, | ||
91 | vp->reg.parm.filterQ, | ||
92 | vp->reg.parm.chorus, | ||
93 | vp->reg.parm.reverb); | ||
94 | snd_iprintf(buf, "avol=%x, acutoff=%x, apitch=%x\n", | ||
95 | vp->avol, vp->acutoff, vp->apitch); | ||
96 | snd_iprintf(buf, "apan=%x, aaux=%x, ptarget=%x, vtarget=%x, ftarget=%x\n", | ||
97 | vp->apan, vp->aaux, | ||
98 | vp->ptarget, | ||
99 | vp->vtarget, | ||
100 | vp->ftarget); | ||
101 | snd_iprintf(buf, "start=%x, end=%x, loopstart=%x, loopend=%x\n", | ||
102 | vp->reg.start, vp->reg.end, vp->reg.loopstart, vp->reg.loopend); | ||
103 | snd_iprintf(buf, "sample_mode=%x, rate=%x\n", vp->reg.sample_mode, vp->reg.rate_offset); | ||
104 | } | ||
105 | #endif | ||
106 | up(&emu->register_mutex); | ||
107 | } | ||
108 | |||
109 | |||
110 | void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device) | ||
111 | { | ||
112 | snd_info_entry_t *entry; | ||
113 | char name[64]; | ||
114 | |||
115 | sprintf(name, "wavetableD%d", device); | ||
116 | entry = snd_info_create_card_entry(card, name, card->proc_root); | ||
117 | if (entry == NULL) | ||
118 | return; | ||
119 | |||
120 | entry->content = SNDRV_INFO_CONTENT_TEXT; | ||
121 | entry->private_data = emu; | ||
122 | entry->c.text.read_size = 1024; | ||
123 | entry->c.text.read = snd_emux_proc_info_read; | ||
124 | if (snd_info_register(entry) < 0) | ||
125 | snd_info_free_entry(entry); | ||
126 | else | ||
127 | emu->proc = entry; | ||
128 | } | ||
129 | |||
130 | void snd_emux_proc_free(snd_emux_t *emu) | ||
131 | { | ||
132 | if (emu->proc) { | ||
133 | snd_info_unregister(emu->proc); | ||
134 | emu->proc = NULL; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | #endif /* CONFIG_PROC_FS */ | ||
diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c new file mode 100644 index 000000000000..e41b28d9bf52 --- /dev/null +++ b/sound/synth/emux/emux_seq.c | |||
@@ -0,0 +1,428 @@ | |||
1 | /* | ||
2 | * Midi Sequencer interface routines. | ||
3 | * | ||
4 | * Copyright (C) 1999 Steve Ratcliffe | ||
5 | * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include "emux_voice.h" | ||
23 | #include <linux/slab.h> | ||
24 | |||
25 | |||
26 | /* Prototypes for static functions */ | ||
27 | static void free_port(void *private); | ||
28 | static void snd_emux_init_port(snd_emux_port_t *p); | ||
29 | static int snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info); | ||
30 | static int snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info); | ||
31 | static int get_client(snd_card_t *card, int index, char *name); | ||
32 | |||
33 | /* | ||
34 | * MIDI emulation operators | ||
35 | */ | ||
36 | static snd_midi_op_t emux_ops = { | ||
37 | snd_emux_note_on, | ||
38 | snd_emux_note_off, | ||
39 | snd_emux_key_press, | ||
40 | snd_emux_terminate_note, | ||
41 | snd_emux_control, | ||
42 | snd_emux_nrpn, | ||
43 | snd_emux_sysex, | ||
44 | }; | ||
45 | |||
46 | |||
47 | /* | ||
48 | * number of MIDI channels | ||
49 | */ | ||
50 | #define MIDI_CHANNELS 16 | ||
51 | |||
52 | /* | ||
53 | * type flags for MIDI sequencer port | ||
54 | */ | ||
55 | #define DEFAULT_MIDI_TYPE (SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |\ | ||
56 | SNDRV_SEQ_PORT_TYPE_MIDI_GM |\ | ||
57 | SNDRV_SEQ_PORT_TYPE_MIDI_GS |\ | ||
58 | SNDRV_SEQ_PORT_TYPE_MIDI_XG |\ | ||
59 | SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE) | ||
60 | |||
61 | /* | ||
62 | * Initialise the EMUX Synth by creating a client and registering | ||
63 | * a series of ports. | ||
64 | * Each of the ports will contain the 16 midi channels. Applications | ||
65 | * can connect to these ports to play midi data. | ||
66 | */ | ||
67 | int | ||
68 | snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index) | ||
69 | { | ||
70 | int i; | ||
71 | snd_seq_port_callback_t pinfo; | ||
72 | char tmpname[64]; | ||
73 | |||
74 | sprintf(tmpname, "%s WaveTable", emu->name); | ||
75 | emu->client = get_client(card, index, tmpname); | ||
76 | if (emu->client < 0) { | ||
77 | snd_printk("can't create client\n"); | ||
78 | return -ENODEV; | ||
79 | } | ||
80 | |||
81 | if (emu->num_ports < 0) { | ||
82 | snd_printk("seqports must be greater than zero\n"); | ||
83 | emu->num_ports = 1; | ||
84 | } else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) { | ||
85 | snd_printk("too many ports." | ||
86 | "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS); | ||
87 | emu->num_ports = SNDRV_EMUX_MAX_PORTS; | ||
88 | } | ||
89 | |||
90 | memset(&pinfo, 0, sizeof(pinfo)); | ||
91 | pinfo.owner = THIS_MODULE; | ||
92 | pinfo.use = snd_emux_use; | ||
93 | pinfo.unuse = snd_emux_unuse; | ||
94 | pinfo.event_input = snd_emux_event_input; | ||
95 | |||
96 | for (i = 0; i < emu->num_ports; i++) { | ||
97 | snd_emux_port_t *p; | ||
98 | |||
99 | sprintf(tmpname, "%s Port %d", emu->name, i); | ||
100 | p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS, | ||
101 | 0, &pinfo); | ||
102 | if (p == NULL) { | ||
103 | snd_printk("can't create port\n"); | ||
104 | return -ENOMEM; | ||
105 | } | ||
106 | |||
107 | p->port_mode = SNDRV_EMUX_PORT_MODE_MIDI; | ||
108 | snd_emux_init_port(p); | ||
109 | emu->ports[i] = p->chset.port; | ||
110 | emu->portptrs[i] = p; | ||
111 | } | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | |||
117 | /* | ||
118 | * Detach from the ports that were set up for this synthesizer and | ||
119 | * destroy the kernel client. | ||
120 | */ | ||
121 | void | ||
122 | snd_emux_detach_seq(snd_emux_t *emu) | ||
123 | { | ||
124 | if (emu->voices) | ||
125 | snd_emux_terminate_all(emu); | ||
126 | |||
127 | down(&emu->register_mutex); | ||
128 | if (emu->client >= 0) { | ||
129 | snd_seq_delete_kernel_client(emu->client); | ||
130 | emu->client = -1; | ||
131 | } | ||
132 | up(&emu->register_mutex); | ||
133 | } | ||
134 | |||
135 | |||
136 | /* | ||
137 | * create a sequencer port and channel_set | ||
138 | */ | ||
139 | |||
140 | snd_emux_port_t * | ||
141 | snd_emux_create_port(snd_emux_t *emu, char *name, | ||
142 | int max_channels, int oss_port, | ||
143 | snd_seq_port_callback_t *callback) | ||
144 | { | ||
145 | snd_emux_port_t *p; | ||
146 | int i, type, cap; | ||
147 | |||
148 | /* Allocate structures for this channel */ | ||
149 | if ((p = kcalloc(1, sizeof(*p), GFP_KERNEL)) == NULL) { | ||
150 | snd_printk("no memory\n"); | ||
151 | return NULL; | ||
152 | } | ||
153 | p->chset.channels = kcalloc(max_channels, sizeof(snd_midi_channel_t), GFP_KERNEL); | ||
154 | if (p->chset.channels == NULL) { | ||
155 | snd_printk("no memory\n"); | ||
156 | kfree(p); | ||
157 | return NULL; | ||
158 | } | ||
159 | for (i = 0; i < max_channels; i++) | ||
160 | p->chset.channels[i].number = i; | ||
161 | p->chset.private_data = p; | ||
162 | p->chset.max_channels = max_channels; | ||
163 | p->emu = emu; | ||
164 | p->chset.client = emu->client; | ||
165 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
166 | snd_emux_create_effect(p); | ||
167 | #endif | ||
168 | callback->private_free = free_port; | ||
169 | callback->private_data = p; | ||
170 | |||
171 | cap = SNDRV_SEQ_PORT_CAP_WRITE; | ||
172 | if (oss_port) { | ||
173 | type = SNDRV_SEQ_PORT_TYPE_SPECIFIC; | ||
174 | } else { | ||
175 | type = DEFAULT_MIDI_TYPE; | ||
176 | cap |= SNDRV_SEQ_PORT_CAP_SUBS_WRITE; | ||
177 | } | ||
178 | |||
179 | p->chset.port = snd_seq_event_port_attach(emu->client, callback, | ||
180 | cap, type, max_channels, | ||
181 | emu->max_voices, name); | ||
182 | |||
183 | return p; | ||
184 | } | ||
185 | |||
186 | |||
187 | /* | ||
188 | * release memory block for port | ||
189 | */ | ||
190 | static void | ||
191 | free_port(void *private_data) | ||
192 | { | ||
193 | snd_emux_port_t *p; | ||
194 | |||
195 | p = private_data; | ||
196 | if (p) { | ||
197 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
198 | snd_emux_delete_effect(p); | ||
199 | #endif | ||
200 | kfree(p->chset.channels); | ||
201 | kfree(p); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | |||
206 | #define DEFAULT_DRUM_FLAGS (1<<9) | ||
207 | |||
208 | /* | ||
209 | * initialize the port specific parameters | ||
210 | */ | ||
211 | static void | ||
212 | snd_emux_init_port(snd_emux_port_t *p) | ||
213 | { | ||
214 | p->drum_flags = DEFAULT_DRUM_FLAGS; | ||
215 | p->volume_atten = 0; | ||
216 | |||
217 | snd_emux_reset_port(p); | ||
218 | } | ||
219 | |||
220 | |||
221 | /* | ||
222 | * reset port | ||
223 | */ | ||
224 | void | ||
225 | snd_emux_reset_port(snd_emux_port_t *port) | ||
226 | { | ||
227 | int i; | ||
228 | |||
229 | /* stop all sounds */ | ||
230 | snd_emux_sounds_off_all(port); | ||
231 | |||
232 | snd_midi_channel_set_clear(&port->chset); | ||
233 | |||
234 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
235 | snd_emux_clear_effect(port); | ||
236 | #endif | ||
237 | |||
238 | /* set port specific control parameters */ | ||
239 | port->ctrls[EMUX_MD_DEF_BANK] = 0; | ||
240 | port->ctrls[EMUX_MD_DEF_DRUM] = 0; | ||
241 | port->ctrls[EMUX_MD_REALTIME_PAN] = 1; | ||
242 | |||
243 | for (i = 0; i < port->chset.max_channels; i++) { | ||
244 | snd_midi_channel_t *chan = port->chset.channels + i; | ||
245 | chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | |||
250 | /* | ||
251 | * input sequencer event | ||
252 | */ | ||
253 | int | ||
254 | snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private_data, | ||
255 | int atomic, int hop) | ||
256 | { | ||
257 | snd_emux_port_t *port; | ||
258 | |||
259 | port = private_data; | ||
260 | snd_assert(port != NULL && ev != NULL, return -EINVAL); | ||
261 | |||
262 | snd_midi_process_event(&emux_ops, ev, &port->chset); | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | |||
268 | /* | ||
269 | * increment usage count | ||
270 | */ | ||
271 | int | ||
272 | snd_emux_inc_count(snd_emux_t *emu) | ||
273 | { | ||
274 | emu->used++; | ||
275 | if (!try_module_get(emu->ops.owner)) | ||
276 | goto __error; | ||
277 | if (!try_module_get(emu->card->module)) { | ||
278 | module_put(emu->ops.owner); | ||
279 | __error: | ||
280 | emu->used--; | ||
281 | return 0; | ||
282 | } | ||
283 | return 1; | ||
284 | } | ||
285 | |||
286 | |||
287 | /* | ||
288 | * decrease usage count | ||
289 | */ | ||
290 | void | ||
291 | snd_emux_dec_count(snd_emux_t *emu) | ||
292 | { | ||
293 | module_put(emu->card->module); | ||
294 | emu->used--; | ||
295 | if (emu->used <= 0) | ||
296 | snd_emux_terminate_all(emu); | ||
297 | module_put(emu->ops.owner); | ||
298 | } | ||
299 | |||
300 | |||
301 | /* | ||
302 | * Routine that is called upon a first use of a particular port | ||
303 | */ | ||
304 | static int | ||
305 | snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info) | ||
306 | { | ||
307 | snd_emux_port_t *p; | ||
308 | snd_emux_t *emu; | ||
309 | |||
310 | p = private_data; | ||
311 | snd_assert(p != NULL, return -EINVAL); | ||
312 | emu = p->emu; | ||
313 | snd_assert(emu != NULL, return -EINVAL); | ||
314 | |||
315 | down(&emu->register_mutex); | ||
316 | snd_emux_init_port(p); | ||
317 | snd_emux_inc_count(emu); | ||
318 | up(&emu->register_mutex); | ||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * Routine that is called upon the last unuse() of a particular port. | ||
324 | */ | ||
325 | static int | ||
326 | snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info) | ||
327 | { | ||
328 | snd_emux_port_t *p; | ||
329 | snd_emux_t *emu; | ||
330 | |||
331 | p = private_data; | ||
332 | snd_assert(p != NULL, return -EINVAL); | ||
333 | emu = p->emu; | ||
334 | snd_assert(emu != NULL, return -EINVAL); | ||
335 | |||
336 | down(&emu->register_mutex); | ||
337 | snd_emux_sounds_off_all(p); | ||
338 | snd_emux_dec_count(emu); | ||
339 | up(&emu->register_mutex); | ||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | |||
344 | /* | ||
345 | * Create a sequencer client | ||
346 | */ | ||
347 | static int | ||
348 | get_client(snd_card_t *card, int index, char *name) | ||
349 | { | ||
350 | snd_seq_client_callback_t callbacks; | ||
351 | snd_seq_client_info_t cinfo; | ||
352 | int client; | ||
353 | |||
354 | memset(&callbacks, 0, sizeof(callbacks)); | ||
355 | callbacks.private_data = NULL; | ||
356 | callbacks.allow_input = 1; | ||
357 | callbacks.allow_output = 1; | ||
358 | |||
359 | /* Find a free client, start from 1 as the MPU expects to use 0 */ | ||
360 | client = snd_seq_create_kernel_client(card, index, &callbacks); | ||
361 | if (client < 0) | ||
362 | return client; | ||
363 | |||
364 | memset(&cinfo, 0, sizeof(cinfo)); | ||
365 | cinfo.client = client; | ||
366 | cinfo.type = KERNEL_CLIENT; | ||
367 | strcpy(cinfo.name, name); | ||
368 | snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); | ||
369 | |||
370 | return client; | ||
371 | } | ||
372 | |||
373 | |||
374 | /* | ||
375 | * attach virtual rawmidi devices | ||
376 | */ | ||
377 | int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card) | ||
378 | { | ||
379 | int i; | ||
380 | |||
381 | emu->vmidi = NULL; | ||
382 | if (emu->midi_ports <= 0) | ||
383 | return 0; | ||
384 | |||
385 | emu->vmidi = kcalloc(emu->midi_ports, sizeof(snd_rawmidi_t*), GFP_KERNEL); | ||
386 | if (emu->vmidi == NULL) | ||
387 | return -ENOMEM; | ||
388 | |||
389 | for (i = 0; i < emu->midi_ports; i++) { | ||
390 | snd_rawmidi_t *rmidi; | ||
391 | snd_virmidi_dev_t *rdev; | ||
392 | if (snd_virmidi_new(card, emu->midi_devidx + i, &rmidi) < 0) | ||
393 | goto __error; | ||
394 | rdev = rmidi->private_data; | ||
395 | sprintf(rmidi->name, "%s Synth MIDI", emu->name); | ||
396 | rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH; | ||
397 | rdev->client = emu->client; | ||
398 | rdev->port = emu->ports[i]; | ||
399 | if (snd_device_register(card, rmidi) < 0) { | ||
400 | snd_device_free(card, rmidi); | ||
401 | goto __error; | ||
402 | } | ||
403 | emu->vmidi[i] = rmidi; | ||
404 | //snd_printk("virmidi %d ok\n", i); | ||
405 | } | ||
406 | return 0; | ||
407 | |||
408 | __error: | ||
409 | //snd_printk("error init..\n"); | ||
410 | snd_emux_delete_virmidi(emu); | ||
411 | return -ENOMEM; | ||
412 | } | ||
413 | |||
414 | int snd_emux_delete_virmidi(snd_emux_t *emu) | ||
415 | { | ||
416 | int i; | ||
417 | |||
418 | if (emu->vmidi == NULL) | ||
419 | return 0; | ||
420 | |||
421 | for (i = 0; i < emu->midi_ports; i++) { | ||
422 | if (emu->vmidi[i]) | ||
423 | snd_device_free(emu->card, emu->vmidi[i]); | ||
424 | } | ||
425 | kfree(emu->vmidi); | ||
426 | emu->vmidi = NULL; | ||
427 | return 0; | ||
428 | } | ||
diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c new file mode 100644 index 000000000000..f13b038329eb --- /dev/null +++ b/sound/synth/emux/emux_synth.c | |||
@@ -0,0 +1,963 @@ | |||
1 | /* | ||
2 | * Midi synth routines for the Emu8k/Emu10k1 | ||
3 | * | ||
4 | * Copyright (C) 1999 Steve Ratcliffe | ||
5 | * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
6 | * | ||
7 | * Contains code based on awe_wave.c by Takashi Iwai | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include "emux_voice.h" | ||
26 | #include <sound/asoundef.h> | ||
27 | |||
28 | /* | ||
29 | * Prototypes | ||
30 | */ | ||
31 | |||
32 | /* | ||
33 | * Ensure a value is between two points | ||
34 | * macro evaluates its args more than once, so changed to upper-case. | ||
35 | */ | ||
36 | #define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) | ||
37 | #define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) | ||
38 | |||
39 | static int get_zone(snd_emux_t *emu, snd_emux_port_t *port, int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table); | ||
40 | static int get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan); | ||
41 | static void terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free); | ||
42 | static void exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass); | ||
43 | static void terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free); | ||
44 | static void update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update); | ||
45 | static void setup_voice(snd_emux_voice_t *vp); | ||
46 | static int calc_pan(snd_emux_voice_t *vp); | ||
47 | static int calc_volume(snd_emux_voice_t *vp); | ||
48 | static int calc_pitch(snd_emux_voice_t *vp); | ||
49 | |||
50 | |||
51 | /* | ||
52 | * Start a note. | ||
53 | */ | ||
54 | void | ||
55 | snd_emux_note_on(void *p, int note, int vel, snd_midi_channel_t *chan) | ||
56 | { | ||
57 | snd_emux_t *emu; | ||
58 | int i, key, nvoices; | ||
59 | snd_emux_voice_t *vp; | ||
60 | snd_sf_zone_t *table[SNDRV_EMUX_MAX_MULTI_VOICES]; | ||
61 | unsigned long flags; | ||
62 | snd_emux_port_t *port; | ||
63 | |||
64 | port = p; | ||
65 | snd_assert(port != NULL && chan != NULL, return); | ||
66 | |||
67 | emu = port->emu; | ||
68 | snd_assert(emu != NULL, return); | ||
69 | snd_assert(emu->ops.get_voice != NULL, return); | ||
70 | snd_assert(emu->ops.trigger != NULL, return); | ||
71 | |||
72 | key = note; /* remember the original note */ | ||
73 | nvoices = get_zone(emu, port, ¬e, vel, chan, table); | ||
74 | if (! nvoices) | ||
75 | return; | ||
76 | |||
77 | /* exclusive note off */ | ||
78 | for (i = 0; i < nvoices; i++) { | ||
79 | snd_sf_zone_t *zp = table[i]; | ||
80 | if (zp && zp->v.exclusiveClass) | ||
81 | exclusive_note_off(emu, port, zp->v.exclusiveClass); | ||
82 | } | ||
83 | |||
84 | #if 0 // seems not necessary | ||
85 | /* Turn off the same note on the same channel. */ | ||
86 | terminate_note1(emu, key, chan, 0); | ||
87 | #endif | ||
88 | |||
89 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
90 | for (i = 0; i < nvoices; i++) { | ||
91 | |||
92 | /* set up each voice parameter */ | ||
93 | /* at this stage, we don't trigger the voice yet. */ | ||
94 | |||
95 | if (table[i] == NULL) | ||
96 | continue; | ||
97 | |||
98 | vp = emu->ops.get_voice(emu, port); | ||
99 | if (vp == NULL || vp->ch < 0) | ||
100 | continue; | ||
101 | snd_assert(vp->emu != NULL && vp->hw != NULL, return); | ||
102 | if (STATE_IS_PLAYING(vp->state)) | ||
103 | emu->ops.terminate(vp); | ||
104 | |||
105 | vp->time = emu->use_time++; | ||
106 | vp->chan = chan; | ||
107 | vp->port = port; | ||
108 | vp->key = key; | ||
109 | vp->note = note; | ||
110 | vp->velocity = vel; | ||
111 | vp->zone = table[i]; | ||
112 | if (vp->zone->sample) | ||
113 | vp->block = vp->zone->sample->block; | ||
114 | else | ||
115 | vp->block = NULL; | ||
116 | |||
117 | setup_voice(vp); | ||
118 | |||
119 | vp->state = SNDRV_EMUX_ST_STANDBY; | ||
120 | if (emu->ops.prepare) { | ||
121 | vp->state = SNDRV_EMUX_ST_OFF; | ||
122 | if (emu->ops.prepare(vp) >= 0) | ||
123 | vp->state = SNDRV_EMUX_ST_STANDBY; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | /* start envelope now */ | ||
128 | for (i = 0; i < emu->max_voices; i++) { | ||
129 | vp = &emu->voices[i]; | ||
130 | if (vp->state == SNDRV_EMUX_ST_STANDBY && | ||
131 | vp->chan == chan) { | ||
132 | emu->ops.trigger(vp); | ||
133 | vp->state = SNDRV_EMUX_ST_ON; | ||
134 | vp->ontime = jiffies; /* remember the trigger timing */ | ||
135 | } | ||
136 | } | ||
137 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
138 | |||
139 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
140 | if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) { | ||
141 | /* clear voice position for the next note on this channel */ | ||
142 | snd_emux_effect_table_t *fx = chan->private; | ||
143 | if (fx) { | ||
144 | fx->flag[EMUX_FX_SAMPLE_START] = 0; | ||
145 | fx->flag[EMUX_FX_COARSE_SAMPLE_START] = 0; | ||
146 | } | ||
147 | } | ||
148 | #endif | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Release a note in response to a midi note off. | ||
153 | */ | ||
154 | void | ||
155 | snd_emux_note_off(void *p, int note, int vel, snd_midi_channel_t *chan) | ||
156 | { | ||
157 | int ch; | ||
158 | snd_emux_t *emu; | ||
159 | snd_emux_voice_t *vp; | ||
160 | unsigned long flags; | ||
161 | snd_emux_port_t *port; | ||
162 | |||
163 | port = p; | ||
164 | snd_assert(port != NULL && chan != NULL, return); | ||
165 | |||
166 | emu = port->emu; | ||
167 | snd_assert(emu != NULL, return); | ||
168 | snd_assert(emu->ops.release != NULL, return); | ||
169 | |||
170 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
171 | for (ch = 0; ch < emu->max_voices; ch++) { | ||
172 | vp = &emu->voices[ch]; | ||
173 | if (STATE_IS_PLAYING(vp->state) && | ||
174 | vp->chan == chan && vp->key == note) { | ||
175 | vp->time = emu->use_time++; | ||
176 | vp->state = SNDRV_EMUX_ST_RELEASED; | ||
177 | if (vp->ontime == jiffies) { | ||
178 | /* if note-off is sent too shortly after | ||
179 | * note-on, emuX engine cannot produce the sound | ||
180 | * correctly. so we'll release this note | ||
181 | * a bit later via timer callback. | ||
182 | */ | ||
183 | vp->state = SNDRV_EMUX_ST_PENDING; | ||
184 | if (! emu->timer_active) { | ||
185 | emu->tlist.expires = jiffies + 1; | ||
186 | add_timer(&emu->tlist); | ||
187 | emu->timer_active = 1; | ||
188 | } | ||
189 | } else | ||
190 | /* ok now release the note */ | ||
191 | emu->ops.release(vp); | ||
192 | } | ||
193 | } | ||
194 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * timer callback | ||
199 | * | ||
200 | * release the pending note-offs | ||
201 | */ | ||
202 | void snd_emux_timer_callback(unsigned long data) | ||
203 | { | ||
204 | snd_emux_t *emu = (snd_emux_t*) data; | ||
205 | snd_emux_voice_t *vp; | ||
206 | int ch, do_again = 0; | ||
207 | |||
208 | spin_lock(&emu->voice_lock); | ||
209 | for (ch = 0; ch < emu->max_voices; ch++) { | ||
210 | vp = &emu->voices[ch]; | ||
211 | if (vp->state == SNDRV_EMUX_ST_PENDING) { | ||
212 | if (vp->ontime == jiffies) | ||
213 | do_again++; /* release this at the next interrupt */ | ||
214 | else { | ||
215 | emu->ops.release(vp); | ||
216 | vp->state = SNDRV_EMUX_ST_RELEASED; | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | if (do_again) { | ||
221 | emu->tlist.expires = jiffies + 1; | ||
222 | add_timer(&emu->tlist); | ||
223 | emu->timer_active = 1; | ||
224 | } else | ||
225 | emu->timer_active = 0; | ||
226 | spin_unlock(&emu->voice_lock); | ||
227 | } | ||
228 | |||
229 | /* | ||
230 | * key pressure change | ||
231 | */ | ||
232 | void | ||
233 | snd_emux_key_press(void *p, int note, int vel, snd_midi_channel_t *chan) | ||
234 | { | ||
235 | int ch; | ||
236 | snd_emux_t *emu; | ||
237 | snd_emux_voice_t *vp; | ||
238 | unsigned long flags; | ||
239 | snd_emux_port_t *port; | ||
240 | |||
241 | port = p; | ||
242 | snd_assert(port != NULL && chan != NULL, return); | ||
243 | |||
244 | emu = port->emu; | ||
245 | snd_assert(emu != NULL, return); | ||
246 | snd_assert(emu->ops.update != NULL, return); | ||
247 | |||
248 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
249 | for (ch = 0; ch < emu->max_voices; ch++) { | ||
250 | vp = &emu->voices[ch]; | ||
251 | if (vp->state == SNDRV_EMUX_ST_ON && | ||
252 | vp->chan == chan && vp->key == note) { | ||
253 | vp->velocity = vel; | ||
254 | update_voice(emu, vp, SNDRV_EMUX_UPDATE_VOLUME); | ||
255 | } | ||
256 | } | ||
257 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
258 | } | ||
259 | |||
260 | |||
261 | /* | ||
262 | * Modulate the voices which belong to the channel | ||
263 | */ | ||
264 | void | ||
265 | snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update) | ||
266 | { | ||
267 | snd_emux_t *emu; | ||
268 | snd_emux_voice_t *vp; | ||
269 | int i; | ||
270 | unsigned long flags; | ||
271 | |||
272 | if (! update) | ||
273 | return; | ||
274 | |||
275 | emu = port->emu; | ||
276 | snd_assert(emu != NULL, return); | ||
277 | snd_assert(emu->ops.update != NULL, return); | ||
278 | |||
279 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
280 | for (i = 0; i < emu->max_voices; i++) { | ||
281 | vp = &emu->voices[i]; | ||
282 | if (vp->chan == chan) | ||
283 | update_voice(emu, vp, update); | ||
284 | } | ||
285 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
286 | } | ||
287 | |||
288 | /* | ||
289 | * Modulate all the voices which belong to the port. | ||
290 | */ | ||
291 | void | ||
292 | snd_emux_update_port(snd_emux_port_t *port, int update) | ||
293 | { | ||
294 | snd_emux_t *emu; | ||
295 | snd_emux_voice_t *vp; | ||
296 | int i; | ||
297 | unsigned long flags; | ||
298 | |||
299 | if (! update) | ||
300 | return; | ||
301 | |||
302 | emu = port->emu; | ||
303 | snd_assert(emu != NULL, return); | ||
304 | snd_assert(emu->ops.update != NULL, return); | ||
305 | |||
306 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
307 | for (i = 0; i < emu->max_voices; i++) { | ||
308 | vp = &emu->voices[i]; | ||
309 | if (vp->port == port) | ||
310 | update_voice(emu, vp, update); | ||
311 | } | ||
312 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
313 | } | ||
314 | |||
315 | |||
316 | /* | ||
317 | * Deal with a controler type event. This includes all types of | ||
318 | * control events, not just the midi controllers | ||
319 | */ | ||
320 | void | ||
321 | snd_emux_control(void *p, int type, snd_midi_channel_t *chan) | ||
322 | { | ||
323 | snd_emux_port_t *port; | ||
324 | |||
325 | port = p; | ||
326 | snd_assert(port != NULL && chan != NULL, return); | ||
327 | |||
328 | switch (type) { | ||
329 | case MIDI_CTL_MSB_MAIN_VOLUME: | ||
330 | case MIDI_CTL_MSB_EXPRESSION: | ||
331 | snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_VOLUME); | ||
332 | break; | ||
333 | |||
334 | case MIDI_CTL_MSB_PAN: | ||
335 | snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN); | ||
336 | break; | ||
337 | |||
338 | case MIDI_CTL_SOFT_PEDAL: | ||
339 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
340 | /* FIXME: this is an emulation */ | ||
341 | snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, -160, | ||
342 | EMUX_FX_FLAG_ADD); | ||
343 | #endif | ||
344 | break; | ||
345 | |||
346 | case MIDI_CTL_PITCHBEND: | ||
347 | snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PITCH); | ||
348 | break; | ||
349 | |||
350 | case MIDI_CTL_MSB_MODWHEEL: | ||
351 | case MIDI_CTL_CHAN_PRESSURE: | ||
352 | snd_emux_update_channel(port, chan, | ||
353 | SNDRV_EMUX_UPDATE_FMMOD | | ||
354 | SNDRV_EMUX_UPDATE_FM2FRQ2); | ||
355 | break; | ||
356 | |||
357 | } | ||
358 | |||
359 | if (port->chset.midi_mode == SNDRV_MIDI_MODE_XG) { | ||
360 | snd_emux_xg_control(port, chan, type); | ||
361 | } | ||
362 | } | ||
363 | |||
364 | |||
365 | /* | ||
366 | * terminate note - if free flag is true, free the terminated voice | ||
367 | */ | ||
368 | static void | ||
369 | terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free) | ||
370 | { | ||
371 | int i; | ||
372 | snd_emux_voice_t *vp; | ||
373 | unsigned long flags; | ||
374 | |||
375 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
376 | for (i = 0; i < emu->max_voices; i++) { | ||
377 | vp = &emu->voices[i]; | ||
378 | if (STATE_IS_PLAYING(vp->state) && vp->chan == chan && | ||
379 | vp->key == note) | ||
380 | terminate_voice(emu, vp, free); | ||
381 | } | ||
382 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
383 | } | ||
384 | |||
385 | |||
386 | /* | ||
387 | * terminate note - exported for midi emulation | ||
388 | */ | ||
389 | void | ||
390 | snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan) | ||
391 | { | ||
392 | snd_emux_t *emu; | ||
393 | snd_emux_port_t *port; | ||
394 | |||
395 | port = p; | ||
396 | snd_assert(port != NULL && chan != NULL, return); | ||
397 | |||
398 | emu = port->emu; | ||
399 | snd_assert(emu != NULL, return); | ||
400 | snd_assert(emu->ops.terminate != NULL, return); | ||
401 | |||
402 | terminate_note1(emu, note, chan, 1); | ||
403 | } | ||
404 | |||
405 | |||
406 | /* | ||
407 | * Terminate all the notes | ||
408 | */ | ||
409 | void | ||
410 | snd_emux_terminate_all(snd_emux_t *emu) | ||
411 | { | ||
412 | int i; | ||
413 | snd_emux_voice_t *vp; | ||
414 | unsigned long flags; | ||
415 | |||
416 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
417 | for (i = 0; i < emu->max_voices; i++) { | ||
418 | vp = &emu->voices[i]; | ||
419 | if (STATE_IS_PLAYING(vp->state)) | ||
420 | terminate_voice(emu, vp, 0); | ||
421 | if (vp->state == SNDRV_EMUX_ST_OFF) { | ||
422 | if (emu->ops.free_voice) | ||
423 | emu->ops.free_voice(vp); | ||
424 | if (emu->ops.reset) | ||
425 | emu->ops.reset(emu, i); | ||
426 | } | ||
427 | vp->time = 0; | ||
428 | } | ||
429 | /* initialize allocation time */ | ||
430 | emu->use_time = 0; | ||
431 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
432 | } | ||
433 | |||
434 | |||
435 | /* | ||
436 | * Terminate all voices associated with the given port | ||
437 | */ | ||
438 | void | ||
439 | snd_emux_sounds_off_all(snd_emux_port_t *port) | ||
440 | { | ||
441 | int i; | ||
442 | snd_emux_t *emu; | ||
443 | snd_emux_voice_t *vp; | ||
444 | unsigned long flags; | ||
445 | |||
446 | snd_assert(port != NULL, return); | ||
447 | emu = port->emu; | ||
448 | snd_assert(emu != NULL, return); | ||
449 | snd_assert(emu->ops.terminate != NULL, return); | ||
450 | |||
451 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
452 | for (i = 0; i < emu->max_voices; i++) { | ||
453 | vp = &emu->voices[i]; | ||
454 | if (STATE_IS_PLAYING(vp->state) && | ||
455 | vp->port == port) | ||
456 | terminate_voice(emu, vp, 0); | ||
457 | if (vp->state == SNDRV_EMUX_ST_OFF) { | ||
458 | if (emu->ops.free_voice) | ||
459 | emu->ops.free_voice(vp); | ||
460 | if (emu->ops.reset) | ||
461 | emu->ops.reset(emu, i); | ||
462 | } | ||
463 | } | ||
464 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
465 | } | ||
466 | |||
467 | |||
468 | /* | ||
469 | * Terminate all voices that have the same exclusive class. This | ||
470 | * is mainly for drums. | ||
471 | */ | ||
472 | static void | ||
473 | exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass) | ||
474 | { | ||
475 | snd_emux_voice_t *vp; | ||
476 | int i; | ||
477 | unsigned long flags; | ||
478 | |||
479 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
480 | for (i = 0; i < emu->max_voices; i++) { | ||
481 | vp = &emu->voices[i]; | ||
482 | if (STATE_IS_PLAYING(vp->state) && vp->port == port && | ||
483 | vp->reg.exclusiveClass == exclass) { | ||
484 | terminate_voice(emu, vp, 0); | ||
485 | } | ||
486 | } | ||
487 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
488 | } | ||
489 | |||
490 | /* | ||
491 | * terminate a voice | ||
492 | * if free flag is true, call free_voice after termination | ||
493 | */ | ||
494 | static void | ||
495 | terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free) | ||
496 | { | ||
497 | emu->ops.terminate(vp); | ||
498 | vp->time = emu->use_time++; | ||
499 | vp->chan = NULL; | ||
500 | vp->port = NULL; | ||
501 | vp->zone = NULL; | ||
502 | vp->block = NULL; | ||
503 | vp->state = SNDRV_EMUX_ST_OFF; | ||
504 | if (free && emu->ops.free_voice) | ||
505 | emu->ops.free_voice(vp); | ||
506 | } | ||
507 | |||
508 | |||
509 | /* | ||
510 | * Modulate the voice | ||
511 | */ | ||
512 | static void | ||
513 | update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update) | ||
514 | { | ||
515 | if (!STATE_IS_PLAYING(vp->state)) | ||
516 | return; | ||
517 | |||
518 | if (vp->chan == NULL || vp->port == NULL) | ||
519 | return; | ||
520 | if (update & SNDRV_EMUX_UPDATE_VOLUME) | ||
521 | calc_volume(vp); | ||
522 | if (update & SNDRV_EMUX_UPDATE_PITCH) | ||
523 | calc_pitch(vp); | ||
524 | if (update & SNDRV_EMUX_UPDATE_PAN) { | ||
525 | if (! calc_pan(vp) && (update == SNDRV_EMUX_UPDATE_PAN)) | ||
526 | return; | ||
527 | } | ||
528 | emu->ops.update(vp, update); | ||
529 | } | ||
530 | |||
531 | |||
532 | #if 0 // not used | ||
533 | /* table for volume target calculation */ | ||
534 | static unsigned short voltarget[16] = { | ||
535 | 0xEAC0, 0xE0C8, 0xD740, 0xCE20, 0xC560, 0xBD08, 0xB500, 0xAD58, | ||
536 | 0xA5F8, 0x9EF0, 0x9830, 0x91C0, 0x8B90, 0x85A8, 0x8000, 0x7A90 | ||
537 | }; | ||
538 | #endif | ||
539 | |||
540 | #define LO_BYTE(v) ((v) & 0xff) | ||
541 | #define HI_BYTE(v) (((v) >> 8) & 0xff) | ||
542 | |||
543 | /* | ||
544 | * Sets up the voice structure by calculating some values that | ||
545 | * will be needed later. | ||
546 | */ | ||
547 | static void | ||
548 | setup_voice(snd_emux_voice_t *vp) | ||
549 | { | ||
550 | soundfont_voice_parm_t *parm; | ||
551 | int pitch; | ||
552 | |||
553 | /* copy the original register values */ | ||
554 | vp->reg = vp->zone->v; | ||
555 | |||
556 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
557 | snd_emux_setup_effect(vp); | ||
558 | #endif | ||
559 | |||
560 | /* reset status */ | ||
561 | vp->apan = -1; | ||
562 | vp->avol = -1; | ||
563 | vp->apitch = -1; | ||
564 | |||
565 | calc_volume(vp); | ||
566 | calc_pitch(vp); | ||
567 | calc_pan(vp); | ||
568 | |||
569 | parm = &vp->reg.parm; | ||
570 | |||
571 | /* compute filter target and correct modulation parameters */ | ||
572 | if (LO_BYTE(parm->modatkhld) >= 0x80 && parm->moddelay >= 0x8000) { | ||
573 | parm->moddelay = 0xbfff; | ||
574 | pitch = (HI_BYTE(parm->pefe) << 4) + vp->apitch; | ||
575 | if (pitch > 0xffff) | ||
576 | pitch = 0xffff; | ||
577 | /* calculate filter target */ | ||
578 | vp->ftarget = parm->cutoff + LO_BYTE(parm->pefe); | ||
579 | LIMITVALUE(vp->ftarget, 0, 255); | ||
580 | vp->ftarget <<= 8; | ||
581 | } else { | ||
582 | vp->ftarget = parm->cutoff; | ||
583 | vp->ftarget <<= 8; | ||
584 | pitch = vp->apitch; | ||
585 | } | ||
586 | |||
587 | /* compute pitch target */ | ||
588 | if (pitch != 0xffff) { | ||
589 | vp->ptarget = 1 << (pitch >> 12); | ||
590 | if (pitch & 0x800) vp->ptarget += (vp->ptarget*0x102e)/0x2710; | ||
591 | if (pitch & 0x400) vp->ptarget += (vp->ptarget*0x764)/0x2710; | ||
592 | if (pitch & 0x200) vp->ptarget += (vp->ptarget*0x389)/0x2710; | ||
593 | vp->ptarget += (vp->ptarget >> 1); | ||
594 | if (vp->ptarget > 0xffff) vp->ptarget = 0xffff; | ||
595 | } else | ||
596 | vp->ptarget = 0xffff; | ||
597 | |||
598 | if (LO_BYTE(parm->modatkhld) >= 0x80) { | ||
599 | parm->modatkhld &= ~0xff; | ||
600 | parm->modatkhld |= 0x7f; | ||
601 | } | ||
602 | |||
603 | /* compute volume target and correct volume parameters */ | ||
604 | vp->vtarget = 0; | ||
605 | #if 0 /* FIXME: this leads to some clicks.. */ | ||
606 | if (LO_BYTE(parm->volatkhld) >= 0x80 && parm->voldelay >= 0x8000) { | ||
607 | parm->voldelay = 0xbfff; | ||
608 | vp->vtarget = voltarget[vp->avol % 0x10] >> (vp->avol >> 4); | ||
609 | } | ||
610 | #endif | ||
611 | |||
612 | if (LO_BYTE(parm->volatkhld) >= 0x80) { | ||
613 | parm->volatkhld &= ~0xff; | ||
614 | parm->volatkhld |= 0x7f; | ||
615 | } | ||
616 | } | ||
617 | |||
618 | /* | ||
619 | * calculate pitch parameter | ||
620 | */ | ||
621 | static unsigned char pan_volumes[256] = { | ||
622 | 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x14,0x17,0x1a,0x1d,0x20,0x22,0x25,0x28,0x2a, | ||
623 | 0x2d,0x30,0x32,0x35,0x37,0x3a,0x3c,0x3f,0x41,0x44,0x46,0x49,0x4b,0x4d,0x50,0x52, | ||
624 | 0x54,0x57,0x59,0x5b,0x5d,0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6f,0x71,0x73,0x75, | ||
625 | 0x77,0x79,0x7b,0x7c,0x7e,0x80,0x82,0x84,0x86,0x88,0x89,0x8b,0x8d,0x8f,0x90,0x92, | ||
626 | 0x94,0x96,0x97,0x99,0x9a,0x9c,0x9e,0x9f,0xa1,0xa2,0xa4,0xa5,0xa7,0xa8,0xaa,0xab, | ||
627 | 0xad,0xae,0xaf,0xb1,0xb2,0xb3,0xb5,0xb6,0xb7,0xb9,0xba,0xbb,0xbc,0xbe,0xbf,0xc0, | ||
628 | 0xc1,0xc2,0xc3,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,0xd0,0xd1, | ||
629 | 0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdc,0xdd,0xde,0xdf, | ||
630 | 0xdf,0xe0,0xe1,0xe2,0xe2,0xe3,0xe4,0xe4,0xe5,0xe6,0xe6,0xe7,0xe8,0xe8,0xe9,0xe9, | ||
631 | 0xea,0xeb,0xeb,0xec,0xec,0xed,0xed,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf1,0xf1,0xf1, | ||
632 | 0xf2,0xf2,0xf3,0xf3,0xf3,0xf4,0xf4,0xf5,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,0xf7, | ||
633 | 0xf7,0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfb,0xfb,0xfb, | ||
634 | 0xfb,0xfb,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd, | ||
635 | 0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe, | ||
636 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, | ||
637 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, | ||
638 | }; | ||
639 | |||
640 | static int | ||
641 | calc_pan(snd_emux_voice_t *vp) | ||
642 | { | ||
643 | snd_midi_channel_t *chan = vp->chan; | ||
644 | int pan; | ||
645 | |||
646 | /* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */ | ||
647 | if (vp->reg.fixpan > 0) /* 0-127 */ | ||
648 | pan = 255 - (int)vp->reg.fixpan * 2; | ||
649 | else { | ||
650 | pan = chan->control[MIDI_CTL_MSB_PAN] - 64; | ||
651 | if (vp->reg.pan >= 0) /* 0-127 */ | ||
652 | pan += vp->reg.pan - 64; | ||
653 | pan = 127 - (int)pan * 2; | ||
654 | } | ||
655 | LIMITVALUE(pan, 0, 255); | ||
656 | |||
657 | if (vp->emu->linear_panning) { | ||
658 | /* assuming linear volume */ | ||
659 | if (pan != vp->apan) { | ||
660 | vp->apan = pan; | ||
661 | if (pan == 0) | ||
662 | vp->aaux = 0xff; | ||
663 | else | ||
664 | vp->aaux = (-pan) & 0xff; | ||
665 | return 1; | ||
666 | } else | ||
667 | return 0; | ||
668 | } else { | ||
669 | /* using volume table */ | ||
670 | if (vp->apan != (int)pan_volumes[pan]) { | ||
671 | vp->apan = pan_volumes[pan]; | ||
672 | vp->aaux = pan_volumes[255 - pan]; | ||
673 | return 1; | ||
674 | } | ||
675 | return 0; | ||
676 | } | ||
677 | } | ||
678 | |||
679 | |||
680 | /* | ||
681 | * calculate volume attenuation | ||
682 | * | ||
683 | * Voice volume is controlled by volume attenuation parameter. | ||
684 | * So volume becomes maximum when avol is 0 (no attenuation), and | ||
685 | * minimum when 255 (-96dB or silence). | ||
686 | */ | ||
687 | |||
688 | /* tables for volume->attenuation calculation */ | ||
689 | static unsigned char voltab1[128] = { | ||
690 | 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, | ||
691 | 0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, | ||
692 | 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, | ||
693 | 0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, | ||
694 | 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, | ||
695 | 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, | ||
696 | 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, | ||
697 | 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, | ||
698 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, | ||
699 | 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, | ||
700 | 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, | ||
701 | 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, | ||
702 | 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
703 | }; | ||
704 | |||
705 | static unsigned char voltab2[128] = { | ||
706 | 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a, | ||
707 | 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21, | ||
708 | 0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a, | ||
709 | 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15, | ||
710 | 0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, | ||
711 | 0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, | ||
712 | 0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, | ||
713 | 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, | ||
714 | 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, | ||
715 | 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, | ||
716 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, | ||
717 | 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, | ||
718 | 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
719 | }; | ||
720 | |||
721 | static unsigned char expressiontab[128] = { | ||
722 | 0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42, | ||
723 | 0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30, | ||
724 | 0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, | ||
725 | 0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e, | ||
726 | 0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18, | ||
727 | 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13, | ||
728 | 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f, | ||
729 | 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, | ||
730 | 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, | ||
731 | 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, | ||
732 | 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, | ||
733 | 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, | ||
734 | 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
735 | }; | ||
736 | |||
737 | /* | ||
738 | * Magic to calculate the volume (actually attenuation) from all the | ||
739 | * voice and channels parameters. | ||
740 | */ | ||
741 | static int | ||
742 | calc_volume(snd_emux_voice_t *vp) | ||
743 | { | ||
744 | int vol; | ||
745 | int main_vol, expression_vol, master_vol; | ||
746 | snd_midi_channel_t *chan = vp->chan; | ||
747 | snd_emux_port_t *port = vp->port; | ||
748 | |||
749 | expression_vol = chan->control[MIDI_CTL_MSB_EXPRESSION]; | ||
750 | LIMITMAX(vp->velocity, 127); | ||
751 | LIMITVALUE(expression_vol, 0, 127); | ||
752 | if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) { | ||
753 | /* 0 - 127 */ | ||
754 | main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME]; | ||
755 | vol = (vp->velocity * main_vol * expression_vol) / (127*127); | ||
756 | vol = vol * vp->reg.amplitude / 127; | ||
757 | |||
758 | LIMITVALUE(vol, 0, 127); | ||
759 | |||
760 | /* calc to attenuation */ | ||
761 | vol = snd_sf_vol_table[vol]; | ||
762 | |||
763 | } else { | ||
764 | main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME] * vp->reg.amplitude / 127; | ||
765 | LIMITVALUE(main_vol, 0, 127); | ||
766 | |||
767 | vol = voltab1[main_vol] + voltab2[vp->velocity]; | ||
768 | vol = (vol * 8) / 3; | ||
769 | vol += vp->reg.attenuation; | ||
770 | vol += ((0x100 - vol) * expressiontab[expression_vol])/128; | ||
771 | } | ||
772 | |||
773 | master_vol = port->chset.gs_master_volume; | ||
774 | LIMITVALUE(master_vol, 0, 127); | ||
775 | vol += snd_sf_vol_table[master_vol]; | ||
776 | vol += port->volume_atten; | ||
777 | |||
778 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
779 | if (chan->private) { | ||
780 | snd_emux_effect_table_t *fx = chan->private; | ||
781 | vol += fx->val[EMUX_FX_ATTEN]; | ||
782 | } | ||
783 | #endif | ||
784 | |||
785 | LIMITVALUE(vol, 0, 255); | ||
786 | if (vp->avol == vol) | ||
787 | return 0; /* value unchanged */ | ||
788 | |||
789 | vp->avol = vol; | ||
790 | if (!SF_IS_DRUM_BANK(get_bank(port, chan)) | ||
791 | && LO_BYTE(vp->reg.parm.volatkhld) < 0x7d) { | ||
792 | int atten; | ||
793 | if (vp->velocity < 70) | ||
794 | atten = 70; | ||
795 | else | ||
796 | atten = vp->velocity; | ||
797 | vp->acutoff = (atten * vp->reg.parm.cutoff + 0xa0) >> 7; | ||
798 | } else { | ||
799 | vp->acutoff = vp->reg.parm.cutoff; | ||
800 | } | ||
801 | |||
802 | return 1; /* value changed */ | ||
803 | } | ||
804 | |||
805 | /* | ||
806 | * calculate pitch offset | ||
807 | * | ||
808 | * 0xE000 is no pitch offset at 44100Hz sample. | ||
809 | * Every 4096 is one octave. | ||
810 | */ | ||
811 | |||
812 | static int | ||
813 | calc_pitch(snd_emux_voice_t *vp) | ||
814 | { | ||
815 | snd_midi_channel_t *chan = vp->chan; | ||
816 | int offset; | ||
817 | |||
818 | /* calculate offset */ | ||
819 | if (vp->reg.fixkey >= 0) { | ||
820 | offset = (vp->reg.fixkey - vp->reg.root) * 4096 / 12; | ||
821 | } else { | ||
822 | offset = (vp->note - vp->reg.root) * 4096 / 12; | ||
823 | } | ||
824 | offset = (offset * vp->reg.scaleTuning) / 100; | ||
825 | offset += vp->reg.tune * 4096 / 1200; | ||
826 | if (chan->midi_pitchbend != 0) { | ||
827 | /* (128 * 8192: 1 semitone) ==> (4096: 12 semitones) */ | ||
828 | offset += chan->midi_pitchbend * chan->gm_rpn_pitch_bend_range / 3072; | ||
829 | } | ||
830 | |||
831 | /* tuning via RPN: | ||
832 | * coarse = -8192 to 8192 (100 cent per 128) | ||
833 | * fine = -8192 to 8192 (max=100cent) | ||
834 | */ | ||
835 | /* 4096 = 1200 cents in emu8000 parameter */ | ||
836 | offset += chan->gm_rpn_coarse_tuning * 4096 / (12 * 128); | ||
837 | offset += chan->gm_rpn_fine_tuning / 24; | ||
838 | |||
839 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
840 | /* add initial pitch correction */ | ||
841 | if (chan->private) { | ||
842 | snd_emux_effect_table_t *fx = chan->private; | ||
843 | if (fx->flag[EMUX_FX_INIT_PITCH]) | ||
844 | offset += fx->val[EMUX_FX_INIT_PITCH]; | ||
845 | } | ||
846 | #endif | ||
847 | |||
848 | /* 0xe000: root pitch */ | ||
849 | offset += 0xe000 + vp->reg.rate_offset; | ||
850 | offset += vp->emu->pitch_shift; | ||
851 | LIMITVALUE(offset, 0, 0xffff); | ||
852 | if (offset == vp->apitch) | ||
853 | return 0; /* unchanged */ | ||
854 | vp->apitch = offset; | ||
855 | return 1; /* value changed */ | ||
856 | } | ||
857 | |||
858 | /* | ||
859 | * Get the bank number assigned to the channel | ||
860 | */ | ||
861 | static int | ||
862 | get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan) | ||
863 | { | ||
864 | int val; | ||
865 | |||
866 | switch (port->chset.midi_mode) { | ||
867 | case SNDRV_MIDI_MODE_XG: | ||
868 | val = chan->control[MIDI_CTL_MSB_BANK]; | ||
869 | if (val == 127) | ||
870 | return 128; /* return drum bank */ | ||
871 | return chan->control[MIDI_CTL_LSB_BANK]; | ||
872 | |||
873 | case SNDRV_MIDI_MODE_GS: | ||
874 | if (chan->drum_channel) | ||
875 | return 128; | ||
876 | /* ignore LSB (bank map) */ | ||
877 | return chan->control[MIDI_CTL_MSB_BANK]; | ||
878 | |||
879 | default: | ||
880 | if (chan->drum_channel) | ||
881 | return 128; | ||
882 | return chan->control[MIDI_CTL_MSB_BANK]; | ||
883 | } | ||
884 | } | ||
885 | |||
886 | |||
887 | /* Look for the zones matching with the given note and velocity. | ||
888 | * The resultant zones are stored on table. | ||
889 | */ | ||
890 | static int | ||
891 | get_zone(snd_emux_t *emu, snd_emux_port_t *port, | ||
892 | int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table) | ||
893 | { | ||
894 | int preset, bank, def_preset, def_bank; | ||
895 | |||
896 | bank = get_bank(port, chan); | ||
897 | preset = chan->midi_program; | ||
898 | |||
899 | if (SF_IS_DRUM_BANK(bank)) { | ||
900 | def_preset = port->ctrls[EMUX_MD_DEF_DRUM]; | ||
901 | def_bank = bank; | ||
902 | } else { | ||
903 | def_preset = preset; | ||
904 | def_bank = port->ctrls[EMUX_MD_DEF_BANK]; | ||
905 | } | ||
906 | |||
907 | return snd_soundfont_search_zone(emu->sflist, notep, vel, preset, bank, | ||
908 | def_preset, def_bank, | ||
909 | table, SNDRV_EMUX_MAX_MULTI_VOICES); | ||
910 | } | ||
911 | |||
912 | /* | ||
913 | */ | ||
914 | void | ||
915 | snd_emux_init_voices(snd_emux_t *emu) | ||
916 | { | ||
917 | snd_emux_voice_t *vp; | ||
918 | int i; | ||
919 | unsigned long flags; | ||
920 | |||
921 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
922 | for (i = 0; i < emu->max_voices; i++) { | ||
923 | vp = &emu->voices[i]; | ||
924 | vp->ch = -1; /* not used */ | ||
925 | vp->state = SNDRV_EMUX_ST_OFF; | ||
926 | vp->chan = NULL; | ||
927 | vp->port = NULL; | ||
928 | vp->time = 0; | ||
929 | vp->emu = emu; | ||
930 | vp->hw = emu->hw; | ||
931 | } | ||
932 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
933 | } | ||
934 | |||
935 | /* | ||
936 | */ | ||
937 | void snd_emux_lock_voice(snd_emux_t *emu, int voice) | ||
938 | { | ||
939 | unsigned long flags; | ||
940 | |||
941 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
942 | if (emu->voices[voice].state == SNDRV_EMUX_ST_OFF) | ||
943 | emu->voices[voice].state = SNDRV_EMUX_ST_LOCKED; | ||
944 | else | ||
945 | snd_printk("invalid voice for lock %d (state = %x)\n", | ||
946 | voice, emu->voices[voice].state); | ||
947 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
948 | } | ||
949 | |||
950 | /* | ||
951 | */ | ||
952 | void snd_emux_unlock_voice(snd_emux_t *emu, int voice) | ||
953 | { | ||
954 | unsigned long flags; | ||
955 | |||
956 | spin_lock_irqsave(&emu->voice_lock, flags); | ||
957 | if (emu->voices[voice].state == SNDRV_EMUX_ST_LOCKED) | ||
958 | emu->voices[voice].state = SNDRV_EMUX_ST_OFF; | ||
959 | else | ||
960 | snd_printk("invalid voice for unlock %d (state = %x)\n", | ||
961 | voice, emu->voices[voice].state); | ||
962 | spin_unlock_irqrestore(&emu->voice_lock, flags); | ||
963 | } | ||
diff --git a/sound/synth/emux/emux_voice.h b/sound/synth/emux/emux_voice.h new file mode 100644 index 000000000000..67eb55348cdf --- /dev/null +++ b/sound/synth/emux/emux_voice.h | |||
@@ -0,0 +1,88 @@ | |||
1 | #ifndef __EMUX_VOICE_H | ||
2 | #define __EMUX_VOICE_H | ||
3 | |||
4 | /* | ||
5 | * A structure to keep track of each hardware voice | ||
6 | * | ||
7 | * Copyright (C) 1999 Steve Ratcliffe | ||
8 | * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include <sound/driver.h> | ||
26 | #include <linux/wait.h> | ||
27 | #include <linux/sched.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/emux_synth.h> | ||
30 | |||
31 | /* Prototypes for emux_seq.c */ | ||
32 | int snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index); | ||
33 | void snd_emux_detach_seq(snd_emux_t *emu); | ||
34 | snd_emux_port_t *snd_emux_create_port(snd_emux_t *emu, char *name, int max_channels, int type, snd_seq_port_callback_t *callback); | ||
35 | void snd_emux_reset_port(snd_emux_port_t *port); | ||
36 | int snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private, int atomic, int hop); | ||
37 | int snd_emux_inc_count(snd_emux_t *emu); | ||
38 | void snd_emux_dec_count(snd_emux_t *emu); | ||
39 | int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card); | ||
40 | int snd_emux_delete_virmidi(snd_emux_t *emu); | ||
41 | |||
42 | /* Prototypes for emux_synth.c */ | ||
43 | void snd_emux_init_voices(snd_emux_t *emu); | ||
44 | |||
45 | void snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan); | ||
46 | void snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan); | ||
47 | void snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan); | ||
48 | void snd_emux_terminate_note(void *p, int note, snd_midi_channel_t *chan); | ||
49 | void snd_emux_control(void *p, int type, struct snd_midi_channel *chan); | ||
50 | |||
51 | void snd_emux_sounds_off_all(snd_emux_port_t *port); | ||
52 | void snd_emux_update_channel(snd_emux_port_t *port, snd_midi_channel_t *chan, int update); | ||
53 | void snd_emux_update_port(snd_emux_port_t *port, int update); | ||
54 | |||
55 | void snd_emux_timer_callback(unsigned long data); | ||
56 | |||
57 | /* emux_effect.c */ | ||
58 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
59 | void snd_emux_create_effect(snd_emux_port_t *p); | ||
60 | void snd_emux_delete_effect(snd_emux_port_t *p); | ||
61 | void snd_emux_clear_effect(snd_emux_port_t *p); | ||
62 | void snd_emux_setup_effect(snd_emux_voice_t *vp); | ||
63 | void snd_emux_send_effect_oss(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val); | ||
64 | void snd_emux_send_effect(snd_emux_port_t *port, snd_midi_channel_t *chan, int type, int val, int mode); | ||
65 | #endif | ||
66 | |||
67 | /* emux_nrpn.c */ | ||
68 | void snd_emux_sysex(void *private_data, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset); | ||
69 | int snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param); | ||
70 | void snd_emux_nrpn(void *private_data, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset); | ||
71 | |||
72 | /* emux_oss.c */ | ||
73 | void snd_emux_init_seq_oss(snd_emux_t *emu); | ||
74 | void snd_emux_detach_seq_oss(snd_emux_t *emu); | ||
75 | |||
76 | /* emux_proc.c */ | ||
77 | #ifdef CONFIG_PROC_FS | ||
78 | void snd_emux_proc_init(snd_emux_t *emu, snd_card_t *card, int device); | ||
79 | void snd_emux_proc_free(snd_emux_t *emu); | ||
80 | #endif | ||
81 | |||
82 | #define STATE_IS_PLAYING(s) ((s) & SNDRV_EMUX_ST_ON) | ||
83 | |||
84 | /* emux_hwdep.c */ | ||
85 | int snd_emux_init_hwdep(snd_emux_t *emu); | ||
86 | void snd_emux_delete_hwdep(snd_emux_t *emu); | ||
87 | |||
88 | #endif | ||
diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c new file mode 100644 index 000000000000..901a7db05bde --- /dev/null +++ b/sound/synth/emux/soundfont.c | |||
@@ -0,0 +1,1462 @@ | |||
1 | /* | ||
2 | * Soundfont generic routines. | ||
3 | * It is intended that these should be used by any driver that is willing | ||
4 | * to accept soundfont patches. | ||
5 | * | ||
6 | * Copyright (C) 1999 Steve Ratcliffe | ||
7 | * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | /* | ||
24 | * Deal with reading in of a soundfont. Code follows the OSS way | ||
25 | * of doing things so that the old sfxload utility can be used. | ||
26 | * Everything may change when there is an alsa way of doing things. | ||
27 | */ | ||
28 | #include <sound/driver.h> | ||
29 | #include <asm/uaccess.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <sound/core.h> | ||
32 | #include <sound/soundfont.h> | ||
33 | #include <sound/seq_oss_legacy.h> | ||
34 | |||
35 | /* Prototypes for static functions */ | ||
36 | |||
37 | static int open_patch(snd_sf_list_t *sflist, const char __user *data, int count, int client); | ||
38 | static snd_soundfont_t *newsf(snd_sf_list_t *sflist, int type, char *name); | ||
39 | static int is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name); | ||
40 | static int close_patch(snd_sf_list_t *sflist); | ||
41 | static int probe_data(snd_sf_list_t *sflist, int sample_id); | ||
42 | static void set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp); | ||
43 | static snd_sf_zone_t *sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf); | ||
44 | static void set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp); | ||
45 | static snd_sf_sample_t *sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf); | ||
46 | static void sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp); | ||
47 | static int load_map(snd_sf_list_t *sflist, const void __user *data, int count); | ||
48 | static int load_info(snd_sf_list_t *sflist, const void __user *data, long count); | ||
49 | static int remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr); | ||
50 | static void init_voice_info(soundfont_voice_info_t *avp); | ||
51 | static void init_voice_parm(soundfont_voice_parm_t *pp); | ||
52 | static snd_sf_sample_t *set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp); | ||
53 | static snd_sf_sample_t *find_sample(snd_soundfont_t *sf, int sample_id); | ||
54 | static int load_data(snd_sf_list_t *sflist, const void __user *data, long count); | ||
55 | static void rebuild_presets(snd_sf_list_t *sflist); | ||
56 | static void add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur); | ||
57 | static void delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp); | ||
58 | static snd_sf_zone_t *search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key); | ||
59 | static int search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level); | ||
60 | static int get_index(int bank, int instr, int key); | ||
61 | static void snd_sf_init(snd_sf_list_t *sflist); | ||
62 | static void snd_sf_clear(snd_sf_list_t *sflist); | ||
63 | |||
64 | /* | ||
65 | * lock access to sflist | ||
66 | */ | ||
67 | static void | ||
68 | lock_preset(snd_sf_list_t *sflist) | ||
69 | { | ||
70 | unsigned long flags; | ||
71 | down(&sflist->presets_mutex); | ||
72 | spin_lock_irqsave(&sflist->lock, flags); | ||
73 | sflist->presets_locked = 1; | ||
74 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
75 | } | ||
76 | |||
77 | |||
78 | /* | ||
79 | * remove lock | ||
80 | */ | ||
81 | static void | ||
82 | unlock_preset(snd_sf_list_t *sflist) | ||
83 | { | ||
84 | unsigned long flags; | ||
85 | spin_lock_irqsave(&sflist->lock, flags); | ||
86 | sflist->presets_locked = 0; | ||
87 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
88 | up(&sflist->presets_mutex); | ||
89 | } | ||
90 | |||
91 | |||
92 | /* | ||
93 | * close the patch if the patch was opened by this client. | ||
94 | */ | ||
95 | int | ||
96 | snd_soundfont_close_check(snd_sf_list_t *sflist, int client) | ||
97 | { | ||
98 | unsigned long flags; | ||
99 | spin_lock_irqsave(&sflist->lock, flags); | ||
100 | if (sflist->open_client == client) { | ||
101 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
102 | return close_patch(sflist); | ||
103 | } | ||
104 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | |||
109 | /* | ||
110 | * Deal with a soundfont patch. Any driver could use these routines | ||
111 | * although it was designed for the AWE64. | ||
112 | * | ||
113 | * The sample_write and callargs pararameters allow a callback into | ||
114 | * the actual driver to write sample data to the board or whatever | ||
115 | * it wants to do with it. | ||
116 | */ | ||
117 | int | ||
118 | snd_soundfont_load(snd_sf_list_t *sflist, const void __user *data, long count, int client) | ||
119 | { | ||
120 | soundfont_patch_info_t patch; | ||
121 | unsigned long flags; | ||
122 | int rc; | ||
123 | |||
124 | if (count < (long)sizeof(patch)) { | ||
125 | snd_printk("patch record too small %ld\n", count); | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | if (copy_from_user(&patch, data, sizeof(patch))) | ||
129 | return -EFAULT; | ||
130 | |||
131 | count -= sizeof(patch); | ||
132 | data += sizeof(patch); | ||
133 | |||
134 | if (patch.key != SNDRV_OSS_SOUNDFONT_PATCH) { | ||
135 | snd_printk("'The wrong kind of patch' %x\n", patch.key); | ||
136 | return -EINVAL; | ||
137 | } | ||
138 | if (count < patch.len) { | ||
139 | snd_printk("Patch too short %ld, need %d\n", count, patch.len); | ||
140 | return -EINVAL; | ||
141 | } | ||
142 | if (patch.len < 0) { | ||
143 | snd_printk("poor length %d\n", patch.len); | ||
144 | return -EINVAL; | ||
145 | } | ||
146 | |||
147 | if (patch.type == SNDRV_SFNT_OPEN_PATCH) { | ||
148 | /* grab sflist to open */ | ||
149 | lock_preset(sflist); | ||
150 | rc = open_patch(sflist, data, count, client); | ||
151 | unlock_preset(sflist); | ||
152 | return rc; | ||
153 | } | ||
154 | |||
155 | /* check if other client already opened patch */ | ||
156 | spin_lock_irqsave(&sflist->lock, flags); | ||
157 | if (sflist->open_client != client) { | ||
158 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
159 | return -EBUSY; | ||
160 | } | ||
161 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
162 | |||
163 | lock_preset(sflist); | ||
164 | rc = -EINVAL; | ||
165 | switch (patch.type) { | ||
166 | case SNDRV_SFNT_LOAD_INFO: | ||
167 | rc = load_info(sflist, data, count); | ||
168 | break; | ||
169 | case SNDRV_SFNT_LOAD_DATA: | ||
170 | rc = load_data(sflist, data, count); | ||
171 | break; | ||
172 | case SNDRV_SFNT_CLOSE_PATCH: | ||
173 | rc = close_patch(sflist); | ||
174 | break; | ||
175 | case SNDRV_SFNT_REPLACE_DATA: | ||
176 | /*rc = replace_data(&patch, data, count);*/ | ||
177 | break; | ||
178 | case SNDRV_SFNT_MAP_PRESET: | ||
179 | rc = load_map(sflist, data, count); | ||
180 | break; | ||
181 | case SNDRV_SFNT_PROBE_DATA: | ||
182 | rc = probe_data(sflist, patch.optarg); | ||
183 | break; | ||
184 | case SNDRV_SFNT_REMOVE_INFO: | ||
185 | /* patch must be opened */ | ||
186 | if (sflist->currsf) { | ||
187 | snd_printk("soundfont: remove_info: patch not opened\n"); | ||
188 | rc = -EINVAL; | ||
189 | } else { | ||
190 | int bank, instr; | ||
191 | bank = ((unsigned short)patch.optarg >> 8) & 0xff; | ||
192 | instr = (unsigned short)patch.optarg & 0xff; | ||
193 | if (! remove_info(sflist, sflist->currsf, bank, instr)) | ||
194 | rc = -EINVAL; | ||
195 | else | ||
196 | rc = 0; | ||
197 | } | ||
198 | break; | ||
199 | } | ||
200 | unlock_preset(sflist); | ||
201 | |||
202 | return rc; | ||
203 | } | ||
204 | |||
205 | |||
206 | /* check if specified type is special font (GUS or preset-alias) */ | ||
207 | static inline int | ||
208 | is_special_type(int type) | ||
209 | { | ||
210 | type &= 0x0f; | ||
211 | return (type == SNDRV_SFNT_PAT_TYPE_GUS || | ||
212 | type == SNDRV_SFNT_PAT_TYPE_MAP); | ||
213 | } | ||
214 | |||
215 | |||
216 | /* open patch; create sf list */ | ||
217 | static int | ||
218 | open_patch(snd_sf_list_t *sflist, const char __user *data, int count, int client) | ||
219 | { | ||
220 | soundfont_open_parm_t parm; | ||
221 | snd_soundfont_t *sf; | ||
222 | unsigned long flags; | ||
223 | |||
224 | spin_lock_irqsave(&sflist->lock, flags); | ||
225 | if (sflist->open_client >= 0 || sflist->currsf) { | ||
226 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
227 | return -EBUSY; | ||
228 | } | ||
229 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
230 | |||
231 | if (copy_from_user(&parm, data, sizeof(parm))) | ||
232 | return -EFAULT; | ||
233 | |||
234 | if (is_special_type(parm.type)) { | ||
235 | parm.type |= SNDRV_SFNT_PAT_SHARED; | ||
236 | sf = newsf(sflist, parm.type, NULL); | ||
237 | } else | ||
238 | sf = newsf(sflist, parm.type, parm.name); | ||
239 | if (sf == NULL) { | ||
240 | return -ENOMEM; | ||
241 | } | ||
242 | |||
243 | spin_lock_irqsave(&sflist->lock, flags); | ||
244 | sflist->open_client = client; | ||
245 | sflist->currsf = sf; | ||
246 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | /* | ||
252 | * Allocate a new soundfont structure. | ||
253 | */ | ||
254 | static snd_soundfont_t * | ||
255 | newsf(snd_sf_list_t *sflist, int type, char *name) | ||
256 | { | ||
257 | snd_soundfont_t *sf; | ||
258 | |||
259 | /* check the shared fonts */ | ||
260 | if (type & SNDRV_SFNT_PAT_SHARED) { | ||
261 | for (sf = sflist->fonts; sf; sf = sf->next) { | ||
262 | if (is_identical_font(sf, type, name)) { | ||
263 | return sf; | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | |||
268 | /* not found -- create a new one */ | ||
269 | sf = kcalloc(1, sizeof(*sf), GFP_KERNEL); | ||
270 | if (sf == NULL) | ||
271 | return NULL; | ||
272 | sf->id = sflist->fonts_size; | ||
273 | sflist->fonts_size++; | ||
274 | |||
275 | /* prepend this record */ | ||
276 | sf->next = sflist->fonts; | ||
277 | sflist->fonts = sf; | ||
278 | |||
279 | sf->type = type; | ||
280 | sf->zones = NULL; | ||
281 | sf->samples = NULL; | ||
282 | if (name) | ||
283 | memcpy(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN); | ||
284 | |||
285 | return sf; | ||
286 | } | ||
287 | |||
288 | /* check if the given name matches to the existing list */ | ||
289 | static int | ||
290 | is_identical_font(snd_soundfont_t *sf, int type, unsigned char *name) | ||
291 | { | ||
292 | return ((sf->type & SNDRV_SFNT_PAT_SHARED) && | ||
293 | (sf->type & 0x0f) == (type & 0x0f) && | ||
294 | (name == NULL || | ||
295 | memcmp(sf->name, name, SNDRV_SFNT_PATCH_NAME_LEN) == 0)); | ||
296 | } | ||
297 | |||
298 | /* | ||
299 | * Close the current patch. | ||
300 | */ | ||
301 | static int | ||
302 | close_patch(snd_sf_list_t *sflist) | ||
303 | { | ||
304 | unsigned long flags; | ||
305 | |||
306 | spin_lock_irqsave(&sflist->lock, flags); | ||
307 | sflist->currsf = NULL; | ||
308 | sflist->open_client = -1; | ||
309 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
310 | |||
311 | rebuild_presets(sflist); | ||
312 | |||
313 | return 0; | ||
314 | |||
315 | } | ||
316 | |||
317 | /* probe sample in the current list -- nothing to be loaded */ | ||
318 | static int | ||
319 | probe_data(snd_sf_list_t *sflist, int sample_id) | ||
320 | { | ||
321 | /* patch must be opened */ | ||
322 | if (sflist->currsf) { | ||
323 | /* search the specified sample by optarg */ | ||
324 | if (find_sample(sflist->currsf, sample_id)) | ||
325 | return 0; | ||
326 | } | ||
327 | return -EINVAL; | ||
328 | } | ||
329 | |||
330 | /* | ||
331 | * increment zone counter | ||
332 | */ | ||
333 | static void | ||
334 | set_zone_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_zone_t *zp) | ||
335 | { | ||
336 | zp->counter = sflist->zone_counter++; | ||
337 | if (sf->type & SNDRV_SFNT_PAT_LOCKED) | ||
338 | sflist->zone_locked = sflist->zone_counter; | ||
339 | } | ||
340 | |||
341 | /* | ||
342 | * allocate a new zone record | ||
343 | */ | ||
344 | static snd_sf_zone_t * | ||
345 | sf_zone_new(snd_sf_list_t *sflist, snd_soundfont_t *sf) | ||
346 | { | ||
347 | snd_sf_zone_t *zp; | ||
348 | |||
349 | if ((zp = kcalloc(1, sizeof(*zp), GFP_KERNEL)) == NULL) | ||
350 | return NULL; | ||
351 | zp->next = sf->zones; | ||
352 | sf->zones = zp; | ||
353 | |||
354 | init_voice_info(&zp->v); | ||
355 | |||
356 | set_zone_counter(sflist, sf, zp); | ||
357 | return zp; | ||
358 | } | ||
359 | |||
360 | |||
361 | /* | ||
362 | * increment sample couter | ||
363 | */ | ||
364 | static void | ||
365 | set_sample_counter(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp) | ||
366 | { | ||
367 | sp->counter = sflist->sample_counter++; | ||
368 | if (sf->type & SNDRV_SFNT_PAT_LOCKED) | ||
369 | sflist->sample_locked = sflist->sample_counter; | ||
370 | } | ||
371 | |||
372 | /* | ||
373 | * allocate a new sample list record | ||
374 | */ | ||
375 | static snd_sf_sample_t * | ||
376 | sf_sample_new(snd_sf_list_t *sflist, snd_soundfont_t *sf) | ||
377 | { | ||
378 | snd_sf_sample_t *sp; | ||
379 | |||
380 | if ((sp = kcalloc(1, sizeof(*sp), GFP_KERNEL)) == NULL) | ||
381 | return NULL; | ||
382 | |||
383 | sp->next = sf->samples; | ||
384 | sf->samples = sp; | ||
385 | |||
386 | set_sample_counter(sflist, sf, sp); | ||
387 | return sp; | ||
388 | } | ||
389 | |||
390 | /* | ||
391 | * delete sample list -- this is an exceptional job. | ||
392 | * only the last allocated sample can be deleted. | ||
393 | */ | ||
394 | static void | ||
395 | sf_sample_delete(snd_sf_list_t *sflist, snd_soundfont_t *sf, snd_sf_sample_t *sp) | ||
396 | { | ||
397 | /* only last sample is accepted */ | ||
398 | if (sp == sf->samples) { | ||
399 | sf->samples = sp->next; | ||
400 | kfree(sp); | ||
401 | } | ||
402 | } | ||
403 | |||
404 | |||
405 | /* load voice map */ | ||
406 | static int | ||
407 | load_map(snd_sf_list_t *sflist, const void __user *data, int count) | ||
408 | { | ||
409 | snd_sf_zone_t *zp, *prevp; | ||
410 | snd_soundfont_t *sf; | ||
411 | soundfont_voice_map_t map; | ||
412 | |||
413 | /* get the link info */ | ||
414 | if (count < (int)sizeof(map)) | ||
415 | return -EINVAL; | ||
416 | if (copy_from_user(&map, data, sizeof(map))) | ||
417 | return -EFAULT; | ||
418 | |||
419 | if (map.map_instr < 0 || map.map_instr >= SF_MAX_INSTRUMENTS) | ||
420 | return -EINVAL; | ||
421 | |||
422 | sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_MAP|SNDRV_SFNT_PAT_SHARED, NULL); | ||
423 | if (sf == NULL) | ||
424 | return -ENOMEM; | ||
425 | |||
426 | prevp = NULL; | ||
427 | for (zp = sf->zones; zp; prevp = zp, zp = zp->next) { | ||
428 | if (zp->mapped && | ||
429 | zp->instr == map.map_instr && | ||
430 | zp->bank == map.map_bank && | ||
431 | zp->v.low == map.map_key && | ||
432 | zp->v.start == map.src_instr && | ||
433 | zp->v.end == map.src_bank && | ||
434 | zp->v.fixkey == map.src_key) { | ||
435 | /* the same mapping is already present */ | ||
436 | /* relink this record to the link head */ | ||
437 | if (prevp) { | ||
438 | prevp->next = zp->next; | ||
439 | zp->next = sf->zones; | ||
440 | sf->zones = zp; | ||
441 | } | ||
442 | /* update the counter */ | ||
443 | set_zone_counter(sflist, sf, zp); | ||
444 | return 0; | ||
445 | } | ||
446 | } | ||
447 | |||
448 | /* create a new zone */ | ||
449 | if ((zp = sf_zone_new(sflist, sf)) == NULL) | ||
450 | return -ENOMEM; | ||
451 | |||
452 | zp->bank = map.map_bank; | ||
453 | zp->instr = map.map_instr; | ||
454 | zp->mapped = 1; | ||
455 | if (map.map_key >= 0) { | ||
456 | zp->v.low = map.map_key; | ||
457 | zp->v.high = map.map_key; | ||
458 | } | ||
459 | zp->v.start = map.src_instr; | ||
460 | zp->v.end = map.src_bank; | ||
461 | zp->v.fixkey = map.src_key; | ||
462 | zp->v.sf_id = sf->id; | ||
463 | |||
464 | add_preset(sflist, zp); | ||
465 | |||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | |||
470 | /* remove the present instrument layers */ | ||
471 | static int | ||
472 | remove_info(snd_sf_list_t *sflist, snd_soundfont_t *sf, int bank, int instr) | ||
473 | { | ||
474 | snd_sf_zone_t *prev, *next, *p; | ||
475 | int removed = 0; | ||
476 | |||
477 | prev = NULL; | ||
478 | for (p = sf->zones; p; p = next) { | ||
479 | next = p->next; | ||
480 | if (! p->mapped && | ||
481 | p->bank == bank && p->instr == instr) { | ||
482 | /* remove this layer */ | ||
483 | if (prev) | ||
484 | prev->next = next; | ||
485 | else | ||
486 | sf->zones = next; | ||
487 | removed++; | ||
488 | kfree(p); | ||
489 | } else | ||
490 | prev = p; | ||
491 | } | ||
492 | if (removed) | ||
493 | rebuild_presets(sflist); | ||
494 | return removed; | ||
495 | } | ||
496 | |||
497 | |||
498 | /* | ||
499 | * Read an info record from the user buffer and save it on the current | ||
500 | * open soundfont. | ||
501 | */ | ||
502 | static int | ||
503 | load_info(snd_sf_list_t *sflist, const void __user *data, long count) | ||
504 | { | ||
505 | snd_soundfont_t *sf; | ||
506 | snd_sf_zone_t *zone; | ||
507 | soundfont_voice_rec_hdr_t hdr; | ||
508 | int i; | ||
509 | |||
510 | /* patch must be opened */ | ||
511 | if ((sf = sflist->currsf) == NULL) | ||
512 | return -EINVAL; | ||
513 | |||
514 | if (is_special_type(sf->type)) | ||
515 | return -EINVAL; | ||
516 | |||
517 | if (count < (long)sizeof(hdr)) { | ||
518 | printk("Soundfont error: invalid patch zone length\n"); | ||
519 | return -EINVAL; | ||
520 | } | ||
521 | if (copy_from_user((char*)&hdr, data, sizeof(hdr))) | ||
522 | return -EFAULT; | ||
523 | |||
524 | data += sizeof(hdr); | ||
525 | count -= sizeof(hdr); | ||
526 | |||
527 | if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { | ||
528 | printk("Soundfont error: Illegal voice number %d\n", hdr.nvoices); | ||
529 | return -EINVAL; | ||
530 | } | ||
531 | |||
532 | if (count < (long)sizeof(soundfont_voice_info_t)*hdr.nvoices) { | ||
533 | printk("Soundfont Error: patch length(%ld) is smaller than nvoices(%d)\n", | ||
534 | count, hdr.nvoices); | ||
535 | return -EINVAL; | ||
536 | } | ||
537 | |||
538 | switch (hdr.write_mode) { | ||
539 | case SNDRV_SFNT_WR_EXCLUSIVE: | ||
540 | /* exclusive mode - if the instrument already exists, | ||
541 | return error */ | ||
542 | for (zone = sf->zones; zone; zone = zone->next) { | ||
543 | if (!zone->mapped && | ||
544 | zone->bank == hdr.bank && | ||
545 | zone->instr == hdr.instr) | ||
546 | return -EINVAL; | ||
547 | } | ||
548 | break; | ||
549 | case SNDRV_SFNT_WR_REPLACE: | ||
550 | /* replace mode - remove the instrument if it already exists */ | ||
551 | remove_info(sflist, sf, hdr.bank, hdr.instr); | ||
552 | break; | ||
553 | } | ||
554 | |||
555 | for (i = 0; i < hdr.nvoices; i++) { | ||
556 | snd_sf_zone_t tmpzone; | ||
557 | |||
558 | /* copy awe_voice_info parameters */ | ||
559 | if (copy_from_user(&tmpzone.v, data, sizeof(tmpzone.v))) { | ||
560 | return -EFAULT; | ||
561 | } | ||
562 | |||
563 | data += sizeof(tmpzone.v); | ||
564 | count -= sizeof(tmpzone.v); | ||
565 | |||
566 | tmpzone.bank = hdr.bank; | ||
567 | tmpzone.instr = hdr.instr; | ||
568 | tmpzone.mapped = 0; | ||
569 | tmpzone.v.sf_id = sf->id; | ||
570 | if (tmpzone.v.mode & SNDRV_SFNT_MODE_INIT_PARM) | ||
571 | init_voice_parm(&tmpzone.v.parm); | ||
572 | |||
573 | /* create a new zone */ | ||
574 | if ((zone = sf_zone_new(sflist, sf)) == NULL) { | ||
575 | return -ENOMEM; | ||
576 | } | ||
577 | |||
578 | /* copy the temporary data */ | ||
579 | zone->bank = tmpzone.bank; | ||
580 | zone->instr = tmpzone.instr; | ||
581 | zone->v = tmpzone.v; | ||
582 | |||
583 | /* look up the sample */ | ||
584 | zone->sample = set_sample(sf, &zone->v); | ||
585 | } | ||
586 | |||
587 | return 0; | ||
588 | } | ||
589 | |||
590 | |||
591 | /* initialize voice_info record */ | ||
592 | static void | ||
593 | init_voice_info(soundfont_voice_info_t *avp) | ||
594 | { | ||
595 | memset(avp, 0, sizeof(*avp)); | ||
596 | |||
597 | avp->root = 60; | ||
598 | avp->high = 127; | ||
599 | avp->velhigh = 127; | ||
600 | avp->fixkey = -1; | ||
601 | avp->fixvel = -1; | ||
602 | avp->fixpan = -1; | ||
603 | avp->pan = -1; | ||
604 | avp->amplitude = 127; | ||
605 | avp->scaleTuning = 100; | ||
606 | |||
607 | init_voice_parm(&avp->parm); | ||
608 | } | ||
609 | |||
610 | /* initialize voice_parm record: | ||
611 | * Env1/2: delay=0, attack=0, hold=0, sustain=0, decay=0, release=0. | ||
612 | * Vibrato and Tremolo effects are zero. | ||
613 | * Cutoff is maximum. | ||
614 | * Chorus and Reverb effects are zero. | ||
615 | */ | ||
616 | static void | ||
617 | init_voice_parm(soundfont_voice_parm_t *pp) | ||
618 | { | ||
619 | memset(pp, 0, sizeof(*pp)); | ||
620 | |||
621 | pp->moddelay = 0x8000; | ||
622 | pp->modatkhld = 0x7f7f; | ||
623 | pp->moddcysus = 0x7f7f; | ||
624 | pp->modrelease = 0x807f; | ||
625 | |||
626 | pp->voldelay = 0x8000; | ||
627 | pp->volatkhld = 0x7f7f; | ||
628 | pp->voldcysus = 0x7f7f; | ||
629 | pp->volrelease = 0x807f; | ||
630 | |||
631 | pp->lfo1delay = 0x8000; | ||
632 | pp->lfo2delay = 0x8000; | ||
633 | |||
634 | pp->cutoff = 0xff; | ||
635 | } | ||
636 | |||
637 | /* search the specified sample */ | ||
638 | static snd_sf_sample_t * | ||
639 | set_sample(snd_soundfont_t *sf, soundfont_voice_info_t *avp) | ||
640 | { | ||
641 | snd_sf_sample_t *sample; | ||
642 | |||
643 | sample = find_sample(sf, avp->sample); | ||
644 | if (sample == NULL) | ||
645 | return NULL; | ||
646 | |||
647 | /* add in the actual sample offsets: | ||
648 | * The voice_info addresses define only the relative offset | ||
649 | * from sample pointers. Here we calculate the actual DRAM | ||
650 | * offset from sample pointers. | ||
651 | */ | ||
652 | avp->start += sample->v.start; | ||
653 | avp->end += sample->v.end; | ||
654 | avp->loopstart += sample->v.loopstart; | ||
655 | avp->loopend += sample->v.loopend; | ||
656 | |||
657 | /* copy mode flags */ | ||
658 | avp->sample_mode = sample->v.mode_flags; | ||
659 | |||
660 | return sample; | ||
661 | } | ||
662 | |||
663 | /* find the sample pointer with the given id in the soundfont */ | ||
664 | static snd_sf_sample_t * | ||
665 | find_sample(snd_soundfont_t *sf, int sample_id) | ||
666 | { | ||
667 | snd_sf_sample_t *p; | ||
668 | |||
669 | if (sf == NULL) | ||
670 | return NULL; | ||
671 | |||
672 | for (p = sf->samples; p; p = p->next) { | ||
673 | if (p->v.sample == sample_id) | ||
674 | return p; | ||
675 | } | ||
676 | return NULL; | ||
677 | } | ||
678 | |||
679 | |||
680 | /* | ||
681 | * Load sample information, this can include data to be loaded onto | ||
682 | * the soundcard. It can also just be a pointer into soundcard ROM. | ||
683 | * If there is data it will be written to the soundcard via the callback | ||
684 | * routine. | ||
685 | */ | ||
686 | static int | ||
687 | load_data(snd_sf_list_t *sflist, const void __user *data, long count) | ||
688 | { | ||
689 | snd_soundfont_t *sf; | ||
690 | soundfont_sample_info_t sample_info; | ||
691 | snd_sf_sample_t *sp; | ||
692 | long off; | ||
693 | |||
694 | /* patch must be opened */ | ||
695 | if ((sf = sflist->currsf) == NULL) | ||
696 | return -EINVAL; | ||
697 | |||
698 | if (is_special_type(sf->type)) | ||
699 | return -EINVAL; | ||
700 | |||
701 | if (copy_from_user(&sample_info, data, sizeof(sample_info))) | ||
702 | return -EFAULT; | ||
703 | |||
704 | off = sizeof(sample_info); | ||
705 | |||
706 | if (sample_info.size != (count-off)/2) | ||
707 | return -EINVAL; | ||
708 | |||
709 | /* Check for dup */ | ||
710 | if (find_sample(sf, sample_info.sample)) { | ||
711 | /* if shared sample, skip this data */ | ||
712 | if (sf->type & SNDRV_SFNT_PAT_SHARED) | ||
713 | return 0; | ||
714 | return -EINVAL; | ||
715 | } | ||
716 | |||
717 | /* Allocate a new sample structure */ | ||
718 | if ((sp = sf_sample_new(sflist, sf)) == NULL) | ||
719 | return -ENOMEM; | ||
720 | |||
721 | sp->v = sample_info; | ||
722 | sp->v.sf_id = sf->id; | ||
723 | sp->v.dummy = 0; | ||
724 | sp->v.truesize = sp->v.size; | ||
725 | |||
726 | /* | ||
727 | * If there is wave data then load it. | ||
728 | */ | ||
729 | if (sp->v.size > 0) { | ||
730 | int rc; | ||
731 | rc = sflist->callback.sample_new | ||
732 | (sflist->callback.private_data, sp, sflist->memhdr, | ||
733 | data + off, count - off); | ||
734 | if (rc < 0) { | ||
735 | sf_sample_delete(sflist, sf, sp); | ||
736 | return rc; | ||
737 | } | ||
738 | sflist->mem_used += sp->v.truesize; | ||
739 | } | ||
740 | |||
741 | return count; | ||
742 | } | ||
743 | |||
744 | |||
745 | /* log2_tbl[i] = log2(i+128) * 0x10000 */ | ||
746 | static int log_tbl[129] = { | ||
747 | 0x70000, 0x702df, 0x705b9, 0x7088e, 0x70b5d, 0x70e26, 0x710eb, 0x713aa, | ||
748 | 0x71663, 0x71918, 0x71bc8, 0x71e72, 0x72118, 0x723b9, 0x72655, 0x728ed, | ||
749 | 0x72b80, 0x72e0e, 0x73098, 0x7331d, 0x7359e, 0x7381b, 0x73a93, 0x73d08, | ||
750 | 0x73f78, 0x741e4, 0x7444c, 0x746b0, 0x74910, 0x74b6c, 0x74dc4, 0x75019, | ||
751 | 0x75269, 0x754b6, 0x75700, 0x75946, 0x75b88, 0x75dc7, 0x76002, 0x7623a, | ||
752 | 0x7646e, 0x766a0, 0x768cd, 0x76af8, 0x76d1f, 0x76f43, 0x77164, 0x77382, | ||
753 | 0x7759d, 0x777b4, 0x779c9, 0x77bdb, 0x77dea, 0x77ff5, 0x781fe, 0x78404, | ||
754 | 0x78608, 0x78808, 0x78a06, 0x78c01, 0x78df9, 0x78fef, 0x791e2, 0x793d2, | ||
755 | 0x795c0, 0x797ab, 0x79993, 0x79b79, 0x79d5d, 0x79f3e, 0x7a11d, 0x7a2f9, | ||
756 | 0x7a4d3, 0x7a6ab, 0x7a880, 0x7aa53, 0x7ac24, 0x7adf2, 0x7afbe, 0x7b188, | ||
757 | 0x7b350, 0x7b515, 0x7b6d8, 0x7b899, 0x7ba58, 0x7bc15, 0x7bdd0, 0x7bf89, | ||
758 | 0x7c140, 0x7c2f5, 0x7c4a7, 0x7c658, 0x7c807, 0x7c9b3, 0x7cb5e, 0x7cd07, | ||
759 | 0x7ceae, 0x7d053, 0x7d1f7, 0x7d398, 0x7d538, 0x7d6d6, 0x7d872, 0x7da0c, | ||
760 | 0x7dba4, 0x7dd3b, 0x7ded0, 0x7e063, 0x7e1f4, 0x7e384, 0x7e512, 0x7e69f, | ||
761 | 0x7e829, 0x7e9b3, 0x7eb3a, 0x7ecc0, 0x7ee44, 0x7efc7, 0x7f148, 0x7f2c8, | ||
762 | 0x7f446, 0x7f5c2, 0x7f73d, 0x7f8b7, 0x7fa2f, 0x7fba5, 0x7fd1a, 0x7fe8d, | ||
763 | 0x80000, | ||
764 | }; | ||
765 | |||
766 | /* convert from linear to log value | ||
767 | * | ||
768 | * conversion: value = log2(amount / base) * ratio | ||
769 | * | ||
770 | * argument: | ||
771 | * amount = linear value (unsigned, 32bit max) | ||
772 | * offset = base offset (:= log2(base) * 0x10000) | ||
773 | * ratio = division ratio | ||
774 | * | ||
775 | */ | ||
776 | int | ||
777 | snd_sf_linear_to_log(unsigned int amount, int offset, int ratio) | ||
778 | { | ||
779 | int v; | ||
780 | int s, low, bit; | ||
781 | |||
782 | if (amount < 2) | ||
783 | return 0; | ||
784 | for (bit = 0; ! (amount & 0x80000000L); bit++) | ||
785 | amount <<= 1; | ||
786 | s = (amount >> 24) & 0x7f; | ||
787 | low = (amount >> 16) & 0xff; | ||
788 | /* linear approxmimation by lower 8 bit */ | ||
789 | v = (log_tbl[s + 1] * low + log_tbl[s] * (0x100 - low)) >> 8; | ||
790 | v -= offset; | ||
791 | v = (v * ratio) >> 16; | ||
792 | v += (24 - bit) * ratio; | ||
793 | return v; | ||
794 | } | ||
795 | |||
796 | #define OFFSET_MSEC 653117 /* base = 1000 */ | ||
797 | #define OFFSET_ABSCENT 851781 /* base = 8176 */ | ||
798 | #define OFFSET_SAMPLERATE 1011119 /* base = 44100 */ | ||
799 | |||
800 | #define ABSCENT_RATIO 1200 | ||
801 | #define TIMECENT_RATIO 1200 | ||
802 | #define SAMPLERATE_RATIO 4096 | ||
803 | |||
804 | /* | ||
805 | * mHz to abscent | ||
806 | * conversion: abscent = log2(MHz / 8176) * 1200 | ||
807 | */ | ||
808 | static int | ||
809 | freq_to_note(int mhz) | ||
810 | { | ||
811 | return snd_sf_linear_to_log(mhz, OFFSET_ABSCENT, ABSCENT_RATIO); | ||
812 | } | ||
813 | |||
814 | /* convert Hz to AWE32 rate offset: | ||
815 | * sample pitch offset for the specified sample rate | ||
816 | * rate=44100 is no offset, each 4096 is 1 octave (twice). | ||
817 | * eg, when rate is 22050, this offset becomes -4096. | ||
818 | * | ||
819 | * conversion: offset = log2(Hz / 44100) * 4096 | ||
820 | */ | ||
821 | static int | ||
822 | calc_rate_offset(int hz) | ||
823 | { | ||
824 | return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO); | ||
825 | } | ||
826 | |||
827 | |||
828 | /* calculate GUS envelope time */ | ||
829 | static int | ||
830 | calc_gus_envelope_time(int rate, int start, int end) | ||
831 | { | ||
832 | int r, p, t; | ||
833 | r = (3 - ((rate >> 6) & 3)) * 3; | ||
834 | p = rate & 0x3f; | ||
835 | t = end - start; | ||
836 | if (t < 0) t = -t; | ||
837 | if (13 > r) | ||
838 | t = t << (13 - r); | ||
839 | else | ||
840 | t = t >> (r - 13); | ||
841 | return (t * 10) / (p * 441); | ||
842 | } | ||
843 | |||
844 | /* convert envelope time parameter to soundfont parameters */ | ||
845 | |||
846 | /* attack & decay/release time table (msec) */ | ||
847 | static short attack_time_tbl[128] = { | ||
848 | 32767, 32767, 5989, 4235, 2994, 2518, 2117, 1780, 1497, 1373, 1259, 1154, 1058, 970, 890, 816, | ||
849 | 707, 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, | ||
850 | 361, 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, | ||
851 | 180, 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, | ||
852 | 90, 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, | ||
853 | 45, 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, | ||
854 | 22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, | ||
855 | 11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 6, 0, | ||
856 | }; | ||
857 | |||
858 | static short decay_time_tbl[128] = { | ||
859 | 32767, 32767, 22614, 15990, 11307, 9508, 7995, 6723, 5653, 5184, 4754, 4359, 3997, 3665, 3361, 3082, | ||
860 | 2828, 2765, 2648, 2535, 2428, 2325, 2226, 2132, 2042, 1955, 1872, 1793, 1717, 1644, 1574, 1507, | ||
861 | 1443, 1382, 1324, 1267, 1214, 1162, 1113, 1066, 978, 936, 897, 859, 822, 787, 754, 722, | ||
862 | 691, 662, 634, 607, 581, 557, 533, 510, 489, 468, 448, 429, 411, 393, 377, 361, | ||
863 | 345, 331, 317, 303, 290, 278, 266, 255, 244, 234, 224, 214, 205, 196, 188, 180, | ||
864 | 172, 165, 158, 151, 145, 139, 133, 127, 122, 117, 112, 107, 102, 98, 94, 90, | ||
865 | 86, 82, 79, 75, 72, 69, 66, 63, 61, 58, 56, 53, 51, 49, 47, 45, | ||
866 | 43, 41, 39, 37, 36, 34, 33, 31, 30, 29, 28, 26, 25, 24, 23, 22, | ||
867 | }; | ||
868 | |||
869 | /* delay time = 0x8000 - msec/92 */ | ||
870 | int | ||
871 | snd_sf_calc_parm_hold(int msec) | ||
872 | { | ||
873 | int val = (0x7f * 92 - msec) / 92; | ||
874 | if (val < 1) val = 1; | ||
875 | if (val >= 126) val = 126; | ||
876 | return val; | ||
877 | } | ||
878 | |||
879 | /* search an index for specified time from given time table */ | ||
880 | static int | ||
881 | calc_parm_search(int msec, short *table) | ||
882 | { | ||
883 | int left = 1, right = 127, mid; | ||
884 | while (left < right) { | ||
885 | mid = (left + right) / 2; | ||
886 | if (msec < (int)table[mid]) | ||
887 | left = mid + 1; | ||
888 | else | ||
889 | right = mid; | ||
890 | } | ||
891 | return left; | ||
892 | } | ||
893 | |||
894 | /* attack time: search from time table */ | ||
895 | int | ||
896 | snd_sf_calc_parm_attack(int msec) | ||
897 | { | ||
898 | return calc_parm_search(msec, attack_time_tbl); | ||
899 | } | ||
900 | |||
901 | /* decay/release time: search from time table */ | ||
902 | int | ||
903 | snd_sf_calc_parm_decay(int msec) | ||
904 | { | ||
905 | return calc_parm_search(msec, decay_time_tbl); | ||
906 | } | ||
907 | |||
908 | int snd_sf_vol_table[128] = { | ||
909 | 255,111,95,86,79,74,70,66,63,61,58,56,54,52,50,49, | ||
910 | 47,46,45,43,42,41,40,39,38,37,36,35,34,34,33,32, | ||
911 | 31,31,30,29,29,28,27,27,26,26,25,24,24,23,23,22, | ||
912 | 22,21,21,21,20,20,19,19,18,18,18,17,17,16,16,16, | ||
913 | 15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10, | ||
914 | 10,10,10,9,9,9,8,8,8,8,7,7,7,7,6,6, | ||
915 | 6,6,5,5,5,5,5,4,4,4,4,3,3,3,3,3, | ||
916 | 2,2,2,2,2,1,1,1,1,1,0,0,0,0,0,0, | ||
917 | }; | ||
918 | |||
919 | |||
920 | #define calc_gus_sustain(val) (0x7f - snd_sf_vol_table[(val)/2]) | ||
921 | #define calc_gus_attenuation(val) snd_sf_vol_table[(val)/2] | ||
922 | |||
923 | /* load GUS patch */ | ||
924 | static int | ||
925 | load_guspatch(snd_sf_list_t *sflist, const char __user *data, long count, int client) | ||
926 | { | ||
927 | struct patch_info patch; | ||
928 | snd_soundfont_t *sf; | ||
929 | snd_sf_zone_t *zone; | ||
930 | snd_sf_sample_t *smp; | ||
931 | int note, sample_id; | ||
932 | int rc; | ||
933 | |||
934 | if (count < (long)sizeof(patch)) { | ||
935 | snd_printk("patch record too small %ld\n", count); | ||
936 | return -EINVAL; | ||
937 | } | ||
938 | if (copy_from_user(&patch, data, sizeof(patch))) | ||
939 | return -EFAULT; | ||
940 | |||
941 | count -= sizeof(patch); | ||
942 | data += sizeof(patch); | ||
943 | |||
944 | sf = newsf(sflist, SNDRV_SFNT_PAT_TYPE_GUS|SNDRV_SFNT_PAT_SHARED, NULL); | ||
945 | if (sf == NULL) | ||
946 | return -ENOMEM; | ||
947 | if ((smp = sf_sample_new(sflist, sf)) == NULL) | ||
948 | return -ENOMEM; | ||
949 | sample_id = sflist->sample_counter; | ||
950 | smp->v.sample = sample_id; | ||
951 | smp->v.start = 0; | ||
952 | smp->v.end = patch.len; | ||
953 | smp->v.loopstart = patch.loop_start; | ||
954 | smp->v.loopend = patch.loop_end; | ||
955 | smp->v.size = patch.len; | ||
956 | |||
957 | /* set up mode flags */ | ||
958 | smp->v.mode_flags = 0; | ||
959 | if (!(patch.mode & WAVE_16_BITS)) | ||
960 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_8BITS; | ||
961 | if (patch.mode & WAVE_UNSIGNED) | ||
962 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_UNSIGNED; | ||
963 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_NO_BLANK; | ||
964 | if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) | ||
965 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_SINGLESHOT; | ||
966 | if (patch.mode & WAVE_BIDIR_LOOP) | ||
967 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_BIDIR_LOOP; | ||
968 | if (patch.mode & WAVE_LOOP_BACK) | ||
969 | smp->v.mode_flags |= SNDRV_SFNT_SAMPLE_REVERSE_LOOP; | ||
970 | |||
971 | if (patch.mode & WAVE_16_BITS) { | ||
972 | /* convert to word offsets */ | ||
973 | smp->v.size /= 2; | ||
974 | smp->v.end /= 2; | ||
975 | smp->v.loopstart /= 2; | ||
976 | smp->v.loopend /= 2; | ||
977 | } | ||
978 | /*smp->v.loopend++;*/ | ||
979 | |||
980 | smp->v.dummy = 0; | ||
981 | smp->v.truesize = 0; | ||
982 | smp->v.sf_id = sf->id; | ||
983 | |||
984 | /* set up voice info */ | ||
985 | if ((zone = sf_zone_new(sflist, sf)) == NULL) { | ||
986 | sf_sample_delete(sflist, sf, smp); | ||
987 | return -ENOMEM; | ||
988 | } | ||
989 | |||
990 | /* | ||
991 | * load wave data | ||
992 | */ | ||
993 | if (sflist->callback.sample_new) { | ||
994 | rc = sflist->callback.sample_new | ||
995 | (sflist->callback.private_data, smp, sflist->memhdr, data, count); | ||
996 | if (rc < 0) { | ||
997 | sf_sample_delete(sflist, sf, smp); | ||
998 | return rc; | ||
999 | } | ||
1000 | /* memory offset is updated after */ | ||
1001 | } | ||
1002 | |||
1003 | /* update the memory offset here */ | ||
1004 | sflist->mem_used += smp->v.truesize; | ||
1005 | |||
1006 | zone->v.sample = sample_id; /* the last sample */ | ||
1007 | zone->v.rate_offset = calc_rate_offset(patch.base_freq); | ||
1008 | note = freq_to_note(patch.base_note); | ||
1009 | zone->v.root = note / 100; | ||
1010 | zone->v.tune = -(note % 100); | ||
1011 | zone->v.low = (freq_to_note(patch.low_note) + 99) / 100; | ||
1012 | zone->v.high = freq_to_note(patch.high_note) / 100; | ||
1013 | /* panning position; -128 - 127 => 0-127 */ | ||
1014 | zone->v.pan = (patch.panning + 128) / 2; | ||
1015 | #if 0 | ||
1016 | snd_printk("gus: basefrq=%d (ofs=%d) root=%d,tune=%d, range:%d-%d\n", | ||
1017 | (int)patch.base_freq, zone->v.rate_offset, | ||
1018 | zone->v.root, zone->v.tune, zone->v.low, zone->v.high); | ||
1019 | #endif | ||
1020 | |||
1021 | /* detuning is ignored */ | ||
1022 | /* 6points volume envelope */ | ||
1023 | if (patch.mode & WAVE_ENVELOPES) { | ||
1024 | int attack, hold, decay, release; | ||
1025 | attack = calc_gus_envelope_time | ||
1026 | (patch.env_rate[0], 0, patch.env_offset[0]); | ||
1027 | hold = calc_gus_envelope_time | ||
1028 | (patch.env_rate[1], patch.env_offset[0], | ||
1029 | patch.env_offset[1]); | ||
1030 | decay = calc_gus_envelope_time | ||
1031 | (patch.env_rate[2], patch.env_offset[1], | ||
1032 | patch.env_offset[2]); | ||
1033 | release = calc_gus_envelope_time | ||
1034 | (patch.env_rate[3], patch.env_offset[1], | ||
1035 | patch.env_offset[4]); | ||
1036 | release += calc_gus_envelope_time | ||
1037 | (patch.env_rate[4], patch.env_offset[3], | ||
1038 | patch.env_offset[4]); | ||
1039 | release += calc_gus_envelope_time | ||
1040 | (patch.env_rate[5], patch.env_offset[4], | ||
1041 | patch.env_offset[5]); | ||
1042 | zone->v.parm.volatkhld = | ||
1043 | (snd_sf_calc_parm_hold(hold) << 8) | | ||
1044 | snd_sf_calc_parm_attack(attack); | ||
1045 | zone->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | | ||
1046 | snd_sf_calc_parm_decay(decay); | ||
1047 | zone->v.parm.volrelease = 0x8000 | snd_sf_calc_parm_decay(release); | ||
1048 | zone->v.attenuation = calc_gus_attenuation(patch.env_offset[0]); | ||
1049 | #if 0 | ||
1050 | snd_printk("gus: atkhld=%x, dcysus=%x, volrel=%x, att=%d\n", | ||
1051 | zone->v.parm.volatkhld, | ||
1052 | zone->v.parm.voldcysus, | ||
1053 | zone->v.parm.volrelease, | ||
1054 | zone->v.attenuation); | ||
1055 | #endif | ||
1056 | } | ||
1057 | |||
1058 | /* fast release */ | ||
1059 | if (patch.mode & WAVE_FAST_RELEASE) { | ||
1060 | zone->v.parm.volrelease = 0x807f; | ||
1061 | } | ||
1062 | |||
1063 | /* tremolo effect */ | ||
1064 | if (patch.mode & WAVE_TREMOLO) { | ||
1065 | int rate = (patch.tremolo_rate * 1000 / 38) / 42; | ||
1066 | zone->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; | ||
1067 | } | ||
1068 | /* vibrato effect */ | ||
1069 | if (patch.mode & WAVE_VIBRATO) { | ||
1070 | int rate = (patch.vibrato_rate * 1000 / 38) / 42; | ||
1071 | zone->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; | ||
1072 | } | ||
1073 | |||
1074 | /* scale_freq, scale_factor, volume, and fractions not implemented */ | ||
1075 | |||
1076 | if (!(smp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT)) | ||
1077 | zone->v.mode = SNDRV_SFNT_MODE_LOOPING; | ||
1078 | else | ||
1079 | zone->v.mode = 0; | ||
1080 | |||
1081 | /* append to the tail of the list */ | ||
1082 | /*zone->bank = ctrls[AWE_MD_GUS_BANK];*/ | ||
1083 | zone->bank = 0; | ||
1084 | zone->instr = patch.instr_no; | ||
1085 | zone->mapped = 0; | ||
1086 | zone->v.sf_id = sf->id; | ||
1087 | |||
1088 | zone->sample = set_sample(sf, &zone->v); | ||
1089 | |||
1090 | /* rebuild preset now */ | ||
1091 | add_preset(sflist, zone); | ||
1092 | |||
1093 | return 0; | ||
1094 | } | ||
1095 | |||
1096 | /* load GUS patch */ | ||
1097 | int | ||
1098 | snd_soundfont_load_guspatch(snd_sf_list_t *sflist, const char __user *data, | ||
1099 | long count, int client) | ||
1100 | { | ||
1101 | int rc; | ||
1102 | lock_preset(sflist); | ||
1103 | rc = load_guspatch(sflist, data, count, client); | ||
1104 | unlock_preset(sflist); | ||
1105 | return rc; | ||
1106 | } | ||
1107 | |||
1108 | |||
1109 | /* | ||
1110 | * Rebuild the preset table. This is like a hash table in that it allows | ||
1111 | * quick access to the zone information. For each preset there are zone | ||
1112 | * structures linked by next_instr and by next_zone. Former is the whole | ||
1113 | * link for this preset, and latter is the link for zone (i.e. instrument/ | ||
1114 | * bank/key combination). | ||
1115 | */ | ||
1116 | static void | ||
1117 | rebuild_presets(snd_sf_list_t *sflist) | ||
1118 | { | ||
1119 | snd_soundfont_t *sf; | ||
1120 | snd_sf_zone_t *cur; | ||
1121 | |||
1122 | /* clear preset table */ | ||
1123 | memset(sflist->presets, 0, sizeof(sflist->presets)); | ||
1124 | |||
1125 | /* search all fonts and insert each font */ | ||
1126 | for (sf = sflist->fonts; sf; sf = sf->next) { | ||
1127 | for (cur = sf->zones; cur; cur = cur->next) { | ||
1128 | if (! cur->mapped && cur->sample == NULL) { | ||
1129 | /* try again to search the corresponding sample */ | ||
1130 | cur->sample = set_sample(sf, &cur->v); | ||
1131 | if (cur->sample == NULL) | ||
1132 | continue; | ||
1133 | } | ||
1134 | |||
1135 | add_preset(sflist, cur); | ||
1136 | } | ||
1137 | } | ||
1138 | } | ||
1139 | |||
1140 | |||
1141 | /* | ||
1142 | * add the given zone to preset table | ||
1143 | */ | ||
1144 | static void | ||
1145 | add_preset(snd_sf_list_t *sflist, snd_sf_zone_t *cur) | ||
1146 | { | ||
1147 | snd_sf_zone_t *zone; | ||
1148 | int index; | ||
1149 | |||
1150 | zone = search_first_zone(sflist, cur->bank, cur->instr, cur->v.low); | ||
1151 | if (zone && zone->v.sf_id != cur->v.sf_id) { | ||
1152 | /* different instrument was already defined */ | ||
1153 | snd_sf_zone_t *p; | ||
1154 | /* compare the allocated time */ | ||
1155 | for (p = zone; p; p = p->next_zone) { | ||
1156 | if (p->counter > cur->counter) | ||
1157 | /* the current is older.. skipped */ | ||
1158 | return; | ||
1159 | } | ||
1160 | /* remove old zones */ | ||
1161 | delete_preset(sflist, zone); | ||
1162 | zone = NULL; /* do not forget to clear this! */ | ||
1163 | } | ||
1164 | |||
1165 | /* prepend this zone */ | ||
1166 | if ((index = get_index(cur->bank, cur->instr, cur->v.low)) < 0) | ||
1167 | return; | ||
1168 | cur->next_zone = zone; /* zone link */ | ||
1169 | cur->next_instr = sflist->presets[index]; /* preset table link */ | ||
1170 | sflist->presets[index] = cur; | ||
1171 | } | ||
1172 | |||
1173 | /* | ||
1174 | * delete the given zones from preset_table | ||
1175 | */ | ||
1176 | static void | ||
1177 | delete_preset(snd_sf_list_t *sflist, snd_sf_zone_t *zp) | ||
1178 | { | ||
1179 | int index; | ||
1180 | snd_sf_zone_t *p; | ||
1181 | |||
1182 | if ((index = get_index(zp->bank, zp->instr, zp->v.low)) < 0) | ||
1183 | return; | ||
1184 | for (p = sflist->presets[index]; p; p = p->next_instr) { | ||
1185 | while (p->next_instr == zp) { | ||
1186 | p->next_instr = zp->next_instr; | ||
1187 | zp = zp->next_zone; | ||
1188 | if (zp == NULL) | ||
1189 | return; | ||
1190 | } | ||
1191 | } | ||
1192 | } | ||
1193 | |||
1194 | |||
1195 | /* | ||
1196 | * Search matching zones from preset table. | ||
1197 | * The note can be rewritten by preset mapping (alias). | ||
1198 | * The found zones are stored on 'table' array. max_layers defines | ||
1199 | * the maximum number of elements in this array. | ||
1200 | * This function returns the number of found zones. 0 if not found. | ||
1201 | */ | ||
1202 | int | ||
1203 | snd_soundfont_search_zone(snd_sf_list_t *sflist, int *notep, int vel, | ||
1204 | int preset, int bank, | ||
1205 | int def_preset, int def_bank, | ||
1206 | snd_sf_zone_t **table, int max_layers) | ||
1207 | { | ||
1208 | int nvoices; | ||
1209 | unsigned long flags; | ||
1210 | |||
1211 | /* this function is supposed to be called atomically, | ||
1212 | * so we check the lock. if it's busy, just returns 0 to | ||
1213 | * tell the caller the busy state | ||
1214 | */ | ||
1215 | spin_lock_irqsave(&sflist->lock, flags); | ||
1216 | if (sflist->presets_locked) { | ||
1217 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
1218 | return 0; | ||
1219 | } | ||
1220 | nvoices = search_zones(sflist, notep, vel, preset, bank, table, max_layers, 0); | ||
1221 | if (! nvoices) { | ||
1222 | if (preset != def_preset || bank != def_bank) | ||
1223 | nvoices = search_zones(sflist, notep, vel, def_preset, def_bank, table, max_layers, 0); | ||
1224 | } | ||
1225 | spin_unlock_irqrestore(&sflist->lock, flags); | ||
1226 | return nvoices; | ||
1227 | } | ||
1228 | |||
1229 | |||
1230 | /* | ||
1231 | * search the first matching zone | ||
1232 | */ | ||
1233 | static snd_sf_zone_t * | ||
1234 | search_first_zone(snd_sf_list_t *sflist, int bank, int preset, int key) | ||
1235 | { | ||
1236 | int index; | ||
1237 | snd_sf_zone_t *zp; | ||
1238 | |||
1239 | if ((index = get_index(bank, preset, key)) < 0) | ||
1240 | return NULL; | ||
1241 | for (zp = sflist->presets[index]; zp; zp = zp->next_instr) { | ||
1242 | if (zp->instr == preset && zp->bank == bank) | ||
1243 | return zp; | ||
1244 | } | ||
1245 | return NULL; | ||
1246 | } | ||
1247 | |||
1248 | |||
1249 | /* | ||
1250 | * search matching zones from sflist. can be called recursively. | ||
1251 | */ | ||
1252 | static int | ||
1253 | search_zones(snd_sf_list_t *sflist, int *notep, int vel, int preset, int bank, snd_sf_zone_t **table, int max_layers, int level) | ||
1254 | { | ||
1255 | snd_sf_zone_t *zp; | ||
1256 | int nvoices; | ||
1257 | |||
1258 | zp = search_first_zone(sflist, bank, preset, *notep); | ||
1259 | nvoices = 0; | ||
1260 | for (; zp; zp = zp->next_zone) { | ||
1261 | if (*notep >= zp->v.low && *notep <= zp->v.high && | ||
1262 | vel >= zp->v.vellow && vel <= zp->v.velhigh) { | ||
1263 | if (zp->mapped) { | ||
1264 | /* search preset mapping (aliasing) */ | ||
1265 | int key = zp->v.fixkey; | ||
1266 | preset = zp->v.start; | ||
1267 | bank = zp->v.end; | ||
1268 | |||
1269 | if (level > 5) /* too deep alias level */ | ||
1270 | return 0; | ||
1271 | if (key < 0) | ||
1272 | key = *notep; | ||
1273 | nvoices = search_zones(sflist, &key, vel, | ||
1274 | preset, bank, table, | ||
1275 | max_layers, level + 1); | ||
1276 | if (nvoices > 0) | ||
1277 | *notep = key; | ||
1278 | break; | ||
1279 | } | ||
1280 | table[nvoices++] = zp; | ||
1281 | if (nvoices >= max_layers) | ||
1282 | break; | ||
1283 | } | ||
1284 | } | ||
1285 | |||
1286 | return nvoices; | ||
1287 | } | ||
1288 | |||
1289 | |||
1290 | /* calculate the index of preset table: | ||
1291 | * drums are mapped from 128 to 255 according to its note key. | ||
1292 | * other instruments are mapped from 0 to 127. | ||
1293 | * if the index is out of range, return -1. | ||
1294 | */ | ||
1295 | static int | ||
1296 | get_index(int bank, int instr, int key) | ||
1297 | { | ||
1298 | int index; | ||
1299 | if (SF_IS_DRUM_BANK(bank)) | ||
1300 | index = key + SF_MAX_INSTRUMENTS; | ||
1301 | else | ||
1302 | index = instr; | ||
1303 | index = index % SF_MAX_PRESETS; | ||
1304 | if (index < 0) | ||
1305 | return -1; | ||
1306 | return index; | ||
1307 | } | ||
1308 | |||
1309 | /* | ||
1310 | * Initialise the sflist structure. | ||
1311 | */ | ||
1312 | static void | ||
1313 | snd_sf_init(snd_sf_list_t *sflist) | ||
1314 | { | ||
1315 | memset(sflist->presets, 0, sizeof(sflist->presets)); | ||
1316 | |||
1317 | sflist->mem_used = 0; | ||
1318 | sflist->currsf = NULL; | ||
1319 | sflist->open_client = -1; | ||
1320 | sflist->fonts = NULL; | ||
1321 | sflist->fonts_size = 0; | ||
1322 | sflist->zone_counter = 0; | ||
1323 | sflist->sample_counter = 0; | ||
1324 | sflist->zone_locked = 0; | ||
1325 | sflist->sample_locked = 0; | ||
1326 | } | ||
1327 | |||
1328 | /* | ||
1329 | * Release all list records | ||
1330 | */ | ||
1331 | static void | ||
1332 | snd_sf_clear(snd_sf_list_t *sflist) | ||
1333 | { | ||
1334 | snd_soundfont_t *sf, *nextsf; | ||
1335 | snd_sf_zone_t *zp, *nextzp; | ||
1336 | snd_sf_sample_t *sp, *nextsp; | ||
1337 | |||
1338 | for (sf = sflist->fonts; sf; sf = nextsf) { | ||
1339 | nextsf = sf->next; | ||
1340 | for (zp = sf->zones; zp; zp = nextzp) { | ||
1341 | nextzp = zp->next; | ||
1342 | kfree(zp); | ||
1343 | } | ||
1344 | for (sp = sf->samples; sp; sp = nextsp) { | ||
1345 | nextsp = sp->next; | ||
1346 | if (sflist->callback.sample_free) | ||
1347 | sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr); | ||
1348 | kfree(sp); | ||
1349 | } | ||
1350 | kfree(sf); | ||
1351 | } | ||
1352 | |||
1353 | snd_sf_init(sflist); | ||
1354 | } | ||
1355 | |||
1356 | |||
1357 | /* | ||
1358 | * Create a new sflist structure | ||
1359 | */ | ||
1360 | snd_sf_list_t * | ||
1361 | snd_sf_new(snd_sf_callback_t *callback, snd_util_memhdr_t *hdr) | ||
1362 | { | ||
1363 | snd_sf_list_t *sflist; | ||
1364 | |||
1365 | if ((sflist = kcalloc(1, sizeof(*sflist), GFP_KERNEL)) == NULL) | ||
1366 | return NULL; | ||
1367 | |||
1368 | init_MUTEX(&sflist->presets_mutex); | ||
1369 | spin_lock_init(&sflist->lock); | ||
1370 | sflist->memhdr = hdr; | ||
1371 | |||
1372 | if (callback) | ||
1373 | sflist->callback = *callback; | ||
1374 | |||
1375 | snd_sf_init(sflist); | ||
1376 | return sflist; | ||
1377 | } | ||
1378 | |||
1379 | |||
1380 | /* | ||
1381 | * Free everything allocated off the sflist structure. | ||
1382 | */ | ||
1383 | void | ||
1384 | snd_sf_free(snd_sf_list_t *sflist) | ||
1385 | { | ||
1386 | if (sflist == NULL) | ||
1387 | return; | ||
1388 | |||
1389 | lock_preset(sflist); | ||
1390 | if (sflist->callback.sample_reset) | ||
1391 | sflist->callback.sample_reset(sflist->callback.private_data); | ||
1392 | snd_sf_clear(sflist); | ||
1393 | unlock_preset(sflist); | ||
1394 | |||
1395 | kfree(sflist); | ||
1396 | } | ||
1397 | |||
1398 | /* | ||
1399 | * Remove all samples | ||
1400 | * The soundcard should be silet before calling this function. | ||
1401 | */ | ||
1402 | int | ||
1403 | snd_soundfont_remove_samples(snd_sf_list_t *sflist) | ||
1404 | { | ||
1405 | lock_preset(sflist); | ||
1406 | if (sflist->callback.sample_reset) | ||
1407 | sflist->callback.sample_reset(sflist->callback.private_data); | ||
1408 | snd_sf_clear(sflist); | ||
1409 | unlock_preset(sflist); | ||
1410 | |||
1411 | return 0; | ||
1412 | } | ||
1413 | |||
1414 | /* | ||
1415 | * Remove unlocked samples. | ||
1416 | * The soundcard should be silent before calling this function. | ||
1417 | */ | ||
1418 | int | ||
1419 | snd_soundfont_remove_unlocked(snd_sf_list_t *sflist) | ||
1420 | { | ||
1421 | snd_soundfont_t *sf; | ||
1422 | snd_sf_zone_t *zp, *nextzp; | ||
1423 | snd_sf_sample_t *sp, *nextsp; | ||
1424 | |||
1425 | lock_preset(sflist); | ||
1426 | |||
1427 | if (sflist->callback.sample_reset) | ||
1428 | sflist->callback.sample_reset(sflist->callback.private_data); | ||
1429 | |||
1430 | /* to be sure */ | ||
1431 | memset(sflist->presets, 0, sizeof(sflist->presets)); | ||
1432 | |||
1433 | for (sf = sflist->fonts; sf; sf = sf->next) { | ||
1434 | for (zp = sf->zones; zp; zp = nextzp) { | ||
1435 | if (zp->counter < sflist->zone_locked) | ||
1436 | break; | ||
1437 | nextzp = zp->next; | ||
1438 | sf->zones = nextzp; | ||
1439 | kfree(zp); | ||
1440 | } | ||
1441 | |||
1442 | for (sp = sf->samples; sp; sp = nextsp) { | ||
1443 | if (sp->counter < sflist->sample_locked) | ||
1444 | break; | ||
1445 | nextsp = sp->next; | ||
1446 | sf->samples = nextsp; | ||
1447 | sflist->mem_used -= sp->v.truesize; | ||
1448 | if (sflist->callback.sample_free) | ||
1449 | sflist->callback.sample_free(sflist->callback.private_data, sp, sflist->memhdr); | ||
1450 | kfree(sp); | ||
1451 | } | ||
1452 | } | ||
1453 | |||
1454 | sflist->zone_counter = sflist->zone_locked; | ||
1455 | sflist->sample_counter = sflist->sample_locked; | ||
1456 | |||
1457 | rebuild_presets(sflist); | ||
1458 | |||
1459 | unlock_preset(sflist); | ||
1460 | return 0; | ||
1461 | } | ||
1462 | |||
diff --git a/sound/synth/util_mem.c b/sound/synth/util_mem.c new file mode 100644 index 000000000000..8b131a11e549 --- /dev/null +++ b/sound/synth/util_mem.c | |||
@@ -0,0 +1,207 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de> | ||
3 | * | ||
4 | * Generic memory management routines for soundcard memory allocation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/util_mem.h> | ||
26 | |||
27 | MODULE_AUTHOR("Takashi Iwai"); | ||
28 | MODULE_DESCRIPTION("Generic memory management routines for soundcard memory allocation"); | ||
29 | MODULE_LICENSE("GPL"); | ||
30 | |||
31 | #define get_memblk(p) list_entry(p, snd_util_memblk_t, list) | ||
32 | |||
33 | /* | ||
34 | * create a new memory manager | ||
35 | */ | ||
36 | snd_util_memhdr_t * | ||
37 | snd_util_memhdr_new(int memsize) | ||
38 | { | ||
39 | snd_util_memhdr_t *hdr; | ||
40 | |||
41 | hdr = kcalloc(1, sizeof(*hdr), GFP_KERNEL); | ||
42 | if (hdr == NULL) | ||
43 | return NULL; | ||
44 | hdr->size = memsize; | ||
45 | init_MUTEX(&hdr->block_mutex); | ||
46 | INIT_LIST_HEAD(&hdr->block); | ||
47 | |||
48 | return hdr; | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * free a memory manager | ||
53 | */ | ||
54 | void snd_util_memhdr_free(snd_util_memhdr_t *hdr) | ||
55 | { | ||
56 | struct list_head *p; | ||
57 | |||
58 | snd_assert(hdr != NULL, return); | ||
59 | /* release all blocks */ | ||
60 | while ((p = hdr->block.next) != &hdr->block) { | ||
61 | list_del(p); | ||
62 | kfree(get_memblk(p)); | ||
63 | } | ||
64 | kfree(hdr); | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * allocate a memory block (without mutex) | ||
69 | */ | ||
70 | snd_util_memblk_t * | ||
71 | __snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size) | ||
72 | { | ||
73 | snd_util_memblk_t *blk; | ||
74 | snd_util_unit_t units, prev_offset; | ||
75 | struct list_head *p; | ||
76 | |||
77 | snd_assert(hdr != NULL, return NULL); | ||
78 | snd_assert(size > 0, return NULL); | ||
79 | |||
80 | /* word alignment */ | ||
81 | units = size; | ||
82 | if (units & 1) | ||
83 | units++; | ||
84 | if (units > hdr->size) | ||
85 | return NULL; | ||
86 | |||
87 | /* look for empty block */ | ||
88 | prev_offset = 0; | ||
89 | list_for_each(p, &hdr->block) { | ||
90 | blk = get_memblk(p); | ||
91 | if (blk->offset - prev_offset >= units) | ||
92 | goto __found; | ||
93 | prev_offset = blk->offset + blk->size; | ||
94 | } | ||
95 | if (hdr->size - prev_offset < units) | ||
96 | return NULL; | ||
97 | |||
98 | __found: | ||
99 | return __snd_util_memblk_new(hdr, units, p->prev); | ||
100 | } | ||
101 | |||
102 | |||
103 | /* | ||
104 | * create a new memory block with the given size | ||
105 | * the block is linked next to prev | ||
106 | */ | ||
107 | snd_util_memblk_t * | ||
108 | __snd_util_memblk_new(snd_util_memhdr_t *hdr, snd_util_unit_t units, | ||
109 | struct list_head *prev) | ||
110 | { | ||
111 | snd_util_memblk_t *blk; | ||
112 | |||
113 | blk = kmalloc(sizeof(snd_util_memblk_t) + hdr->block_extra_size, GFP_KERNEL); | ||
114 | if (blk == NULL) | ||
115 | return NULL; | ||
116 | |||
117 | if (! prev || prev == &hdr->block) | ||
118 | blk->offset = 0; | ||
119 | else { | ||
120 | snd_util_memblk_t *p = get_memblk(prev); | ||
121 | blk->offset = p->offset + p->size; | ||
122 | } | ||
123 | blk->size = units; | ||
124 | list_add(&blk->list, prev); | ||
125 | hdr->nblocks++; | ||
126 | hdr->used += units; | ||
127 | return blk; | ||
128 | } | ||
129 | |||
130 | |||
131 | /* | ||
132 | * allocate a memory block (with mutex) | ||
133 | */ | ||
134 | snd_util_memblk_t * | ||
135 | snd_util_mem_alloc(snd_util_memhdr_t *hdr, int size) | ||
136 | { | ||
137 | snd_util_memblk_t *blk; | ||
138 | down(&hdr->block_mutex); | ||
139 | blk = __snd_util_mem_alloc(hdr, size); | ||
140 | up(&hdr->block_mutex); | ||
141 | return blk; | ||
142 | } | ||
143 | |||
144 | |||
145 | /* | ||
146 | * remove the block from linked-list and free resource | ||
147 | * (without mutex) | ||
148 | */ | ||
149 | void | ||
150 | __snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk) | ||
151 | { | ||
152 | list_del(&blk->list); | ||
153 | hdr->nblocks--; | ||
154 | hdr->used -= blk->size; | ||
155 | kfree(blk); | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * free a memory block (with mutex) | ||
160 | */ | ||
161 | int snd_util_mem_free(snd_util_memhdr_t *hdr, snd_util_memblk_t *blk) | ||
162 | { | ||
163 | snd_assert(hdr && blk, return -EINVAL); | ||
164 | |||
165 | down(&hdr->block_mutex); | ||
166 | __snd_util_mem_free(hdr, blk); | ||
167 | up(&hdr->block_mutex); | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | * return available memory size | ||
173 | */ | ||
174 | int snd_util_mem_avail(snd_util_memhdr_t *hdr) | ||
175 | { | ||
176 | unsigned int size; | ||
177 | down(&hdr->block_mutex); | ||
178 | size = hdr->size - hdr->used; | ||
179 | up(&hdr->block_mutex); | ||
180 | return size; | ||
181 | } | ||
182 | |||
183 | |||
184 | EXPORT_SYMBOL(snd_util_memhdr_new); | ||
185 | EXPORT_SYMBOL(snd_util_memhdr_free); | ||
186 | EXPORT_SYMBOL(snd_util_mem_alloc); | ||
187 | EXPORT_SYMBOL(snd_util_mem_free); | ||
188 | EXPORT_SYMBOL(snd_util_mem_avail); | ||
189 | EXPORT_SYMBOL(__snd_util_mem_alloc); | ||
190 | EXPORT_SYMBOL(__snd_util_mem_free); | ||
191 | EXPORT_SYMBOL(__snd_util_memblk_new); | ||
192 | |||
193 | /* | ||
194 | * INIT part | ||
195 | */ | ||
196 | |||
197 | static int __init alsa_util_mem_init(void) | ||
198 | { | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static void __exit alsa_util_mem_exit(void) | ||
203 | { | ||
204 | } | ||
205 | |||
206 | module_init(alsa_util_mem_init) | ||
207 | module_exit(alsa_util_mem_exit) | ||