| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 35 | #define parm_offset(tag)	xoffsetof(struct soundfont_voice_parm *, tag) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 100 | effect_set_byte(unsigned char *valp, struct snd_midi_channel *chan, int type) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 101 | { | 
|  | 102 | short effect; | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 103 | struct snd_emux_effect_table *fx = chan->private; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 121 | effect_set_word(unsigned short *valp, struct snd_midi_channel *chan, int type) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 122 | { | 
|  | 123 | int effect; | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 124 | struct snd_emux_effect_table *fx = chan->private; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 138 | effect_get_offset(struct snd_midi_channel *chan, int lo, int hi, int mode) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 139 | { | 
|  | 140 | int addr = 0; | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 141 | struct snd_emux_effect_table *fx = chan->private; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 156 | snd_emux_send_effect_oss(struct snd_emux_port *port, | 
|  | 157 | struct snd_midi_channel *chan, int type, int val) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 158 | { | 
|  | 159 | int mode; | 
|  | 160 |  | 
|  | 161 | if (type & 0x40) | 
|  | 162 | mode = EMUX_FX_FLAG_OFF; | 
|  | 163 | else if (type & 0x80) | 
|  | 164 | mode = EMUX_FX_FLAG_ADD; | 
|  | 165 | else | 
|  | 166 | mode = EMUX_FX_FLAG_SET; | 
|  | 167 | type &= 0x3f; | 
|  | 168 |  | 
|  | 169 | snd_emux_send_effect(port, chan, type, val, mode); | 
|  | 170 | } | 
|  | 171 | #endif | 
|  | 172 |  | 
|  | 173 | /* Modify the effect value. | 
|  | 174 | * if update is necessary, call emu8000_control | 
|  | 175 | */ | 
|  | 176 | void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 177 | snd_emux_send_effect(struct snd_emux_port *port, struct snd_midi_channel *chan, | 
|  | 178 | int type, int val, int mode) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 179 | { | 
|  | 180 | int i; | 
|  | 181 | int offset; | 
|  | 182 | unsigned char *srcp, *origp; | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 183 | struct snd_emux *emu; | 
|  | 184 | struct snd_emux_effect_table *fx; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 185 | unsigned long flags; | 
|  | 186 |  | 
|  | 187 | emu = port->emu; | 
|  | 188 | fx = chan->private; | 
|  | 189 | if (emu == NULL || fx == NULL) | 
|  | 190 | return; | 
|  | 191 | if (type < 0 || type >= EMUX_NUM_EFFECTS) | 
|  | 192 | return; | 
|  | 193 |  | 
|  | 194 | fx->val[type] = val; | 
|  | 195 | fx->flag[type] = mode; | 
|  | 196 |  | 
|  | 197 | /* do we need to modify the register in realtime ? */ | 
|  | 198 | if (! parm_defs[type].update || (offset = parm_defs[type].offset) < 0) | 
|  | 199 | return; | 
|  | 200 |  | 
|  | 201 | #ifdef SNDRV_LITTLE_ENDIAN | 
|  | 202 | if (parm_defs[type].type & PARM_IS_ALIGN_HI) | 
|  | 203 | offset++; | 
|  | 204 | #else | 
|  | 205 | if (parm_defs[type].type & PARM_IS_ALIGN_LO) | 
|  | 206 | offset++; | 
|  | 207 | #endif | 
|  | 208 | /* modify the register values */ | 
|  | 209 | spin_lock_irqsave(&emu->voice_lock, flags); | 
|  | 210 | for (i = 0; i < emu->max_voices; i++) { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 211 | struct snd_emux_voice *vp = &emu->voices[i]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 212 | if (!STATE_IS_PLAYING(vp->state) || vp->chan != chan) | 
|  | 213 | continue; | 
|  | 214 | srcp = (unsigned char*)&vp->reg.parm + offset; | 
|  | 215 | origp = (unsigned char*)&vp->zone->v.parm + offset; | 
|  | 216 | if (parm_defs[i].type & PARM_IS_BYTE) { | 
|  | 217 | *srcp = *origp; | 
|  | 218 | effect_set_byte(srcp, chan, type); | 
|  | 219 | } else { | 
|  | 220 | *(unsigned short*)srcp = *(unsigned short*)origp; | 
|  | 221 | effect_set_word((unsigned short*)srcp, chan, type); | 
|  | 222 | } | 
|  | 223 | } | 
|  | 224 | spin_unlock_irqrestore(&emu->voice_lock, flags); | 
|  | 225 |  | 
|  | 226 | /* activate them */ | 
|  | 227 | snd_emux_update_channel(port, chan, parm_defs[type].update); | 
|  | 228 | } | 
|  | 229 |  | 
|  | 230 |  | 
|  | 231 | /* copy wavetable registers to voice table */ | 
|  | 232 | void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 233 | snd_emux_setup_effect(struct snd_emux_voice *vp) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 234 | { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 235 | struct snd_midi_channel *chan = vp->chan; | 
|  | 236 | struct snd_emux_effect_table *fx; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 237 | unsigned char *srcp; | 
|  | 238 | int i; | 
|  | 239 |  | 
|  | 240 | if (! (fx = chan->private)) | 
|  | 241 | return; | 
|  | 242 |  | 
|  | 243 | /* modify the register values via effect table */ | 
|  | 244 | for (i = 0; i < EMUX_FX_END; i++) { | 
|  | 245 | int offset; | 
|  | 246 | if (! fx->flag[i] || (offset = parm_defs[i].offset) < 0) | 
|  | 247 | continue; | 
|  | 248 | #ifdef SNDRV_LITTLE_ENDIAN | 
|  | 249 | if (parm_defs[i].type & PARM_IS_ALIGN_HI) | 
|  | 250 | offset++; | 
|  | 251 | #else | 
|  | 252 | if (parm_defs[i].type & PARM_IS_ALIGN_LO) | 
|  | 253 | offset++; | 
|  | 254 | #endif | 
|  | 255 | srcp = (unsigned char*)&vp->reg.parm + offset; | 
|  | 256 | if (parm_defs[i].type & PARM_IS_BYTE) | 
|  | 257 | effect_set_byte(srcp, chan, i); | 
|  | 258 | else | 
|  | 259 | effect_set_word((unsigned short*)srcp, chan, i); | 
|  | 260 | } | 
|  | 261 |  | 
|  | 262 | /* correct sample and loop points */ | 
|  | 263 | vp->reg.start += effect_get_offset(chan, EMUX_FX_SAMPLE_START, | 
|  | 264 | EMUX_FX_COARSE_SAMPLE_START, | 
|  | 265 | vp->reg.sample_mode); | 
|  | 266 |  | 
|  | 267 | vp->reg.loopstart += effect_get_offset(chan, EMUX_FX_LOOP_START, | 
|  | 268 | EMUX_FX_COARSE_LOOP_START, | 
|  | 269 | vp->reg.sample_mode); | 
|  | 270 |  | 
|  | 271 | vp->reg.loopend += effect_get_offset(chan, EMUX_FX_LOOP_END, | 
|  | 272 | EMUX_FX_COARSE_LOOP_END, | 
|  | 273 | vp->reg.sample_mode); | 
|  | 274 | } | 
|  | 275 |  | 
|  | 276 | /* | 
|  | 277 | * effect table | 
|  | 278 | */ | 
|  | 279 | void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 280 | snd_emux_create_effect(struct snd_emux_port *p) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 281 | { | 
|  | 282 | int i; | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 283 | p->effect = kcalloc(p->chset.max_channels, | 
|  | 284 | sizeof(struct snd_emux_effect_table), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 285 | if (p->effect) { | 
|  | 286 | for (i = 0; i < p->chset.max_channels; i++) | 
|  | 287 | p->chset.channels[i].private = p->effect + i; | 
|  | 288 | } else { | 
|  | 289 | for (i = 0; i < p->chset.max_channels; i++) | 
|  | 290 | p->chset.channels[i].private = NULL; | 
|  | 291 | } | 
|  | 292 | } | 
|  | 293 |  | 
|  | 294 | void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 295 | snd_emux_delete_effect(struct snd_emux_port *p) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 296 | { | 
| Jesper Juhl | 4d57277 | 2005-05-30 17:30:32 +0200 | [diff] [blame] | 297 | kfree(p->effect); | 
|  | 298 | p->effect = NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 299 | } | 
|  | 300 |  | 
|  | 301 | void | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 302 | snd_emux_clear_effect(struct snd_emux_port *p) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 303 | { | 
|  | 304 | if (p->effect) { | 
| Takashi Iwai | 03da312 | 2005-11-17 14:24:47 +0100 | [diff] [blame] | 305 | memset(p->effect, 0, sizeof(struct snd_emux_effect_table) * | 
|  | 306 | p->chset.max_channels); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 307 | } | 
|  | 308 | } | 
|  | 309 |  | 
|  | 310 | #endif /* SNDRV_EMUX_USE_RAW_EFFECT */ |