535 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * synth callback routines for the emu8000 (AWE32/64)4 *5 * Copyright (C) 1999 Steve Ratcliffe6 * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>7 */8 9#include "emu8000_local.h"10#include <linux/export.h>11#include <sound/asoundef.h>12 13/*14 * prototypes15 */16static struct snd_emux_voice *get_voice(struct snd_emux *emu,17 struct snd_emux_port *port);18static int start_voice(struct snd_emux_voice *vp);19static void trigger_voice(struct snd_emux_voice *vp);20static void release_voice(struct snd_emux_voice *vp);21static void update_voice(struct snd_emux_voice *vp, int update);22static void reset_voice(struct snd_emux *emu, int ch);23static void terminate_voice(struct snd_emux_voice *vp);24static void sysex(struct snd_emux *emu, char *buf, int len, int parsed,25 struct snd_midi_channel_set *chset);26#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)27static int oss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2);28#endif29static int load_fx(struct snd_emux *emu, int type, int mode,30 const void __user *buf, long len);31 32static void set_pitch(struct snd_emu8000 *hw, struct snd_emux_voice *vp);33static void set_volume(struct snd_emu8000 *hw, struct snd_emux_voice *vp);34static void set_pan(struct snd_emu8000 *hw, struct snd_emux_voice *vp);35static void set_fmmod(struct snd_emu8000 *hw, struct snd_emux_voice *vp);36static void set_tremfreq(struct snd_emu8000 *hw, struct snd_emux_voice *vp);37static void set_fm2frq2(struct snd_emu8000 *hw, struct snd_emux_voice *vp);38static void set_filterQ(struct snd_emu8000 *hw, struct snd_emux_voice *vp);39static void snd_emu8000_tweak_voice(struct snd_emu8000 *emu, int ch);40 41/*42 * Ensure a value is between two points43 * macro evaluates its args more than once, so changed to upper-case.44 */45#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)46#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)47 48 49/*50 * set up operators51 */52static const struct snd_emux_operators emu8000_ops = {53 .owner = THIS_MODULE,54 .get_voice = get_voice,55 .prepare = start_voice,56 .trigger = trigger_voice,57 .release = release_voice,58 .update = update_voice,59 .terminate = terminate_voice,60 .reset = reset_voice,61 .sample_new = snd_emu8000_sample_new,62 .sample_free = snd_emu8000_sample_free,63 .sample_reset = snd_emu8000_sample_reset,64 .load_fx = load_fx,65 .sysex = sysex,66#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)67 .oss_ioctl = oss_ioctl,68#endif69};70 71void72snd_emu8000_ops_setup(struct snd_emu8000 *hw)73{74 hw->emu->ops = emu8000_ops;75}76 77 78 79/*80 * Terminate a voice81 */82static void83release_voice(struct snd_emux_voice *vp)84{85 int dcysusv;86 struct snd_emu8000 *hw;87 88 hw = vp->hw;89 dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;90 EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv);91 dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease;92 EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv);93}94 95 96/*97 */98static void99terminate_voice(struct snd_emux_voice *vp)100{101 struct snd_emu8000 *hw; 102 103 hw = vp->hw;104 EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F);105}106 107 108/*109 */110static void111update_voice(struct snd_emux_voice *vp, int update)112{113 struct snd_emu8000 *hw;114 115 hw = vp->hw;116 if (update & SNDRV_EMUX_UPDATE_VOLUME)117 set_volume(hw, vp);118 if (update & SNDRV_EMUX_UPDATE_PITCH)119 set_pitch(hw, vp);120 if ((update & SNDRV_EMUX_UPDATE_PAN) &&121 vp->port->ctrls[EMUX_MD_REALTIME_PAN])122 set_pan(hw, vp);123 if (update & SNDRV_EMUX_UPDATE_FMMOD)124 set_fmmod(hw, vp);125 if (update & SNDRV_EMUX_UPDATE_TREMFREQ)126 set_tremfreq(hw, vp);127 if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)128 set_fm2frq2(hw, vp);129 if (update & SNDRV_EMUX_UPDATE_Q)130 set_filterQ(hw, vp);131}132 133 134/*135 * Find a channel (voice) within the EMU that is not in use or at least136 * less in use than other channels. Always returns a valid pointer137 * no matter what. If there is a real shortage of voices then one138 * will be cut. Such is life.139 *140 * The channel index (vp->ch) must be initialized in this routine.141 * In Emu8k, it is identical with the array index.142 */143static struct snd_emux_voice *144get_voice(struct snd_emux *emu, struct snd_emux_port *port)145{146 int i;147 struct snd_emux_voice *vp;148 struct snd_emu8000 *hw;149 150 /* what we are looking for, in order of preference */151 enum {152 OFF=0, RELEASED, PLAYING, END153 };154 155 /* Keeps track of what we are finding */156 struct best {157 unsigned int time;158 int voice;159 } best[END];160 struct best *bp;161 162 hw = emu->hw;163 164 for (i = 0; i < END; i++) {165 best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */166 best[i].voice = -1;167 }168 169 /*170 * Go through them all and get a best one to use.171 */172 for (i = 0; i < emu->max_voices; i++) {173 int state, val;174 175 vp = &emu->voices[i];176 state = vp->state;177 178 if (state == SNDRV_EMUX_ST_OFF)179 bp = best + OFF;180 else if (state == SNDRV_EMUX_ST_RELEASED ||181 state == SNDRV_EMUX_ST_PENDING) {182 bp = best + RELEASED;183 val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff;184 if (! val)185 bp = best + OFF;186 }187 else if (state & SNDRV_EMUX_ST_ON)188 bp = best + PLAYING;189 else190 continue;191 192 /* check if sample is finished playing (non-looping only) */193 if (state != SNDRV_EMUX_ST_OFF &&194 (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {195 val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;196 if (val >= vp->reg.loopstart)197 bp = best + OFF;198 }199 200 if (vp->time < bp->time) {201 bp->time = vp->time;202 bp->voice = i;203 }204 }205 206 for (i = 0; i < END; i++) {207 if (best[i].voice >= 0) {208 vp = &emu->voices[best[i].voice];209 vp->ch = best[i].voice;210 return vp;211 }212 }213 214 /* not found */215 return NULL;216}217 218/*219 */220static int221start_voice(struct snd_emux_voice *vp)222{223 unsigned int temp;224 int ch;225 int addr;226 struct snd_midi_channel *chan;227 struct snd_emu8000 *hw;228 229 hw = vp->hw;230 ch = vp->ch;231 chan = vp->chan;232 233 /* channel to be silent and idle */234 EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080);235 EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF);236 EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF);237 EMU8000_PTRX_WRITE(hw, ch, 0);238 EMU8000_CPF_WRITE(hw, ch, 0);239 240 /* set pitch offset */241 set_pitch(hw, vp);242 243 /* set envelope parameters */244 EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay);245 EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld);246 EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus);247 EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay);248 EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld);249 /* decay/sustain parameter for volume envelope is used250 for triggerg the voice */251 252 /* cutoff and volume */253 set_volume(hw, vp);254 255 /* modulation envelope heights */256 EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe);257 258 /* lfo1/2 delay */259 EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay);260 EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay);261 262 /* lfo1 pitch & cutoff shift */263 set_fmmod(hw, vp);264 /* lfo1 volume & freq */265 set_tremfreq(hw, vp);266 /* lfo2 pitch & freq */267 set_fm2frq2(hw, vp);268 /* pan & loop start */269 set_pan(hw, vp);270 271 /* chorus & loop end (chorus 8bit, MSB) */272 addr = vp->reg.loopend - 1;273 temp = vp->reg.parm.chorus;274 temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;275 LIMITMAX(temp, 255);276 temp = (temp <<24) | (unsigned int)addr;277 EMU8000_CSL_WRITE(hw, ch, temp);278 279 /* Q & current address (Q 4bit value, MSB) */280 addr = vp->reg.start - 1;281 temp = vp->reg.parm.filterQ;282 temp = (temp<<28) | (unsigned int)addr;283 EMU8000_CCCA_WRITE(hw, ch, temp);284 285 /* clear unknown registers */286 EMU8000_00A0_WRITE(hw, ch, 0);287 EMU8000_0080_WRITE(hw, ch, 0);288 289 /* reset volume */290 temp = vp->vtarget << 16;291 EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget);292 EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00);293 294 return 0;295}296 297/*298 * Start envelope299 */300static void301trigger_voice(struct snd_emux_voice *vp)302{303 int ch = vp->ch;304 unsigned int temp;305 struct snd_emu8000 *hw;306 307 hw = vp->hw;308 309 /* set reverb and pitch target */310 temp = vp->reg.parm.reverb;311 temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;312 LIMITMAX(temp, 255);313 temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux;314 EMU8000_PTRX_WRITE(hw, ch, temp);315 EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16);316 EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus);317}318 319/*320 * reset voice parameters321 */322static void323reset_voice(struct snd_emux *emu, int ch)324{325 struct snd_emu8000 *hw;326 327 hw = emu->hw;328 EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);329 snd_emu8000_tweak_voice(hw, ch);330}331 332/*333 * Set the pitch of a possibly playing note.334 */335static void336set_pitch(struct snd_emu8000 *hw, struct snd_emux_voice *vp)337{338 EMU8000_IP_WRITE(hw, vp->ch, vp->apitch);339}340 341/*342 * Set the volume of a possibly already playing note343 */344static void345set_volume(struct snd_emu8000 *hw, struct snd_emux_voice *vp)346{347 int ifatn;348 349 ifatn = (unsigned char)vp->acutoff;350 ifatn = (ifatn << 8);351 ifatn |= (unsigned char)vp->avol;352 EMU8000_IFATN_WRITE(hw, vp->ch, ifatn);353}354 355/*356 * Set pan and loop start address.357 */358static void359set_pan(struct snd_emu8000 *hw, struct snd_emux_voice *vp)360{361 unsigned int temp;362 363 temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1);364 EMU8000_PSST_WRITE(hw, vp->ch, temp);365}366 367#define MOD_SENSE 18368 369static void370set_fmmod(struct snd_emu8000 *hw, struct snd_emux_voice *vp)371{372 unsigned short fmmod;373 short pitch;374 unsigned char cutoff;375 int modulation;376 377 pitch = (char)(vp->reg.parm.fmmod>>8);378 cutoff = (vp->reg.parm.fmmod & 0xff);379 modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;380 pitch += (MOD_SENSE * modulation) / 1200;381 LIMITVALUE(pitch, -128, 127);382 fmmod = ((unsigned char)pitch<<8) | cutoff;383 EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod);384}385 386/* set tremolo (lfo1) volume & frequency */387static void388set_tremfreq(struct snd_emu8000 *hw, struct snd_emux_voice *vp)389{390 EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq);391}392 393/* set lfo2 pitch & frequency */394static void395set_fm2frq2(struct snd_emu8000 *hw, struct snd_emux_voice *vp)396{397 unsigned short fm2frq2;398 short pitch;399 unsigned char freq;400 int modulation;401 402 pitch = (char)(vp->reg.parm.fm2frq2>>8);403 freq = vp->reg.parm.fm2frq2 & 0xff;404 modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;405 pitch += (MOD_SENSE * modulation) / 1200;406 LIMITVALUE(pitch, -128, 127);407 fm2frq2 = ((unsigned char)pitch<<8) | freq;408 EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2);409}410 411/* set filterQ */412static void413set_filterQ(struct snd_emu8000 *hw, struct snd_emux_voice *vp)414{415 unsigned int addr;416 addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;417 addr |= (vp->reg.parm.filterQ << 28);418 EMU8000_CCCA_WRITE(hw, vp->ch, addr);419}420 421/*422 * set the envelope & LFO parameters to the default values423 */424static void425snd_emu8000_tweak_voice(struct snd_emu8000 *emu, int i)426{427 /* set all mod/vol envelope shape to minimum */428 EMU8000_ENVVOL_WRITE(emu, i, 0x8000);429 EMU8000_ENVVAL_WRITE(emu, i, 0x8000);430 EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F);431 EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F);432 EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F);433 EMU8000_PEFE_WRITE(emu, i, 0); /* mod envelope height to zero */434 EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */435 EMU8000_LFO2VAL_WRITE(emu, i, 0x8000);436 EMU8000_IP_WRITE(emu, i, 0xE000); /* no pitch shift */437 EMU8000_IFATN_WRITE(emu, i, 0xFF00); /* volume to minimum */438 EMU8000_FMMOD_WRITE(emu, i, 0);439 EMU8000_TREMFRQ_WRITE(emu, i, 0);440 EMU8000_FM2FRQ2_WRITE(emu, i, 0);441}442 443/*444 * sysex callback445 */446static void447sysex(struct snd_emux *emu, char *buf, int len, int parsed, struct snd_midi_channel_set *chset)448{449 struct snd_emu8000 *hw;450 451 hw = emu->hw;452 453 switch (parsed) {454 case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE:455 hw->chorus_mode = chset->gs_chorus_mode;456 snd_emu8000_update_chorus_mode(hw);457 break;458 459 case SNDRV_MIDI_SYSEX_GS_REVERB_MODE:460 hw->reverb_mode = chset->gs_reverb_mode;461 snd_emu8000_update_reverb_mode(hw);462 break;463 }464}465 466 467#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)468/*469 * OSS ioctl callback470 */471static int472oss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2)473{474 struct snd_emu8000 *hw;475 476 hw = emu->hw;477 478 switch (cmd) {479 case _EMUX_OSS_REVERB_MODE:480 hw->reverb_mode = p1;481 snd_emu8000_update_reverb_mode(hw);482 break;483 484 case _EMUX_OSS_CHORUS_MODE:485 hw->chorus_mode = p1;486 snd_emu8000_update_chorus_mode(hw);487 break;488 489 case _EMUX_OSS_INITIALIZE_CHIP:490 /* snd_emu8000_init(hw); */ /*ignored*/491 break;492 493 case _EMUX_OSS_EQUALIZER:494 hw->bass_level = p1;495 hw->treble_level = p2;496 snd_emu8000_update_equalizer(hw);497 break;498 }499 return 0;500}501#endif502 503 504/*505 * additional patch keys506 */507 508#define SNDRV_EMU8000_LOAD_CHORUS_FX 0x10 /* optarg=mode */509#define SNDRV_EMU8000_LOAD_REVERB_FX 0x11 /* optarg=mode */510 511 512/*513 * callback routine514 */515 516static int517load_fx(struct snd_emux *emu, int type, int mode, const void __user *buf, long len)518{519 struct snd_emu8000 *hw;520 hw = emu->hw;521 522 /* skip header */523 buf += 16;524 len -= 16;525 526 switch (type) {527 case SNDRV_EMU8000_LOAD_CHORUS_FX:528 return snd_emu8000_load_chorus_fx(hw, mode, buf, len);529 case SNDRV_EMU8000_LOAD_REVERB_FX:530 return snd_emu8000_load_reverb_fx(hw, mode, buf, len);531 }532 return -EINVAL;533}534 535