162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Midi synth routines for the Emu8k/Emu10k1 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1999 Steve Ratcliffe 662306a36Sopenharmony_ci * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Contains code based on awe_wave.c by Takashi Iwai 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "emux_voice.h" 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#ifdef SNDRV_EMUX_USE_RAW_EFFECT 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * effects table 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define xoffsetof(type,tag) ((long)(&((type)NULL)->tag) - (long)(NULL)) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define parm_offset(tag) xoffsetof(struct soundfont_voice_parm *, tag) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define PARM_IS_BYTE (1 << 0) 2462306a36Sopenharmony_ci#define PARM_IS_WORD (1 << 1) 2562306a36Sopenharmony_ci#define PARM_IS_ALIGNED (3 << 2) 2662306a36Sopenharmony_ci#define PARM_IS_ALIGN_HI (1 << 2) 2762306a36Sopenharmony_ci#define PARM_IS_ALIGN_LO (2 << 2) 2862306a36Sopenharmony_ci#define PARM_IS_SIGNED (1 << 4) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define PARM_WORD (PARM_IS_WORD) 3162306a36Sopenharmony_ci#define PARM_BYTE_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO) 3262306a36Sopenharmony_ci#define PARM_BYTE_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI) 3362306a36Sopenharmony_ci#define PARM_BYTE (PARM_IS_BYTE) 3462306a36Sopenharmony_ci#define PARM_SIGN_LO (PARM_IS_BYTE|PARM_IS_ALIGN_LO|PARM_IS_SIGNED) 3562306a36Sopenharmony_ci#define PARM_SIGN_HI (PARM_IS_BYTE|PARM_IS_ALIGN_HI|PARM_IS_SIGNED) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic struct emux_parm_defs { 3862306a36Sopenharmony_ci int type; /* byte or word */ 3962306a36Sopenharmony_ci int low, high; /* value range */ 4062306a36Sopenharmony_ci long offset; /* offset in parameter record (-1 = not written) */ 4162306a36Sopenharmony_ci int update; /* flgas for real-time update */ 4262306a36Sopenharmony_ci} parm_defs[EMUX_NUM_EFFECTS] = { 4362306a36Sopenharmony_ci {PARM_WORD, 0, 0x8000, parm_offset(moddelay), 0}, /* env1 delay */ 4462306a36Sopenharmony_ci {PARM_BYTE_LO, 1, 0x80, parm_offset(modatkhld), 0}, /* env1 attack */ 4562306a36Sopenharmony_ci {PARM_BYTE_HI, 0, 0x7e, parm_offset(modatkhld), 0}, /* env1 hold */ 4662306a36Sopenharmony_ci {PARM_BYTE_LO, 1, 0x7f, parm_offset(moddcysus), 0}, /* env1 decay */ 4762306a36Sopenharmony_ci {PARM_BYTE_LO, 1, 0x7f, parm_offset(modrelease), 0}, /* env1 release */ 4862306a36Sopenharmony_ci {PARM_BYTE_HI, 0, 0x7f, parm_offset(moddcysus), 0}, /* env1 sustain */ 4962306a36Sopenharmony_ci {PARM_BYTE_HI, 0, 0xff, parm_offset(pefe), 0}, /* env1 pitch */ 5062306a36Sopenharmony_ci {PARM_BYTE_LO, 0, 0xff, parm_offset(pefe), 0}, /* env1 fc */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci {PARM_WORD, 0, 0x8000, parm_offset(voldelay), 0}, /* env2 delay */ 5362306a36Sopenharmony_ci {PARM_BYTE_LO, 1, 0x80, parm_offset(volatkhld), 0}, /* env2 attack */ 5462306a36Sopenharmony_ci {PARM_BYTE_HI, 0, 0x7e, parm_offset(volatkhld), 0}, /* env2 hold */ 5562306a36Sopenharmony_ci {PARM_BYTE_LO, 1, 0x7f, parm_offset(voldcysus), 0}, /* env2 decay */ 5662306a36Sopenharmony_ci {PARM_BYTE_LO, 1, 0x7f, parm_offset(volrelease), 0}, /* env2 release */ 5762306a36Sopenharmony_ci {PARM_BYTE_HI, 0, 0x7f, parm_offset(voldcysus), 0}, /* env2 sustain */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci {PARM_WORD, 0, 0x8000, parm_offset(lfo1delay), 0}, /* lfo1 delay */ 6062306a36Sopenharmony_ci {PARM_BYTE_LO, 0, 0xff, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 freq */ 6162306a36Sopenharmony_ci {PARM_SIGN_HI, -128, 127, parm_offset(tremfrq), SNDRV_EMUX_UPDATE_TREMFREQ}, /* lfo1 vol */ 6262306a36Sopenharmony_ci {PARM_SIGN_HI, -128, 127, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 pitch */ 6362306a36Sopenharmony_ci {PARM_BYTE_LO, 0, 0xff, parm_offset(fmmod), SNDRV_EMUX_UPDATE_FMMOD}, /* lfo1 cutoff */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci {PARM_WORD, 0, 0x8000, parm_offset(lfo2delay), 0}, /* lfo2 delay */ 6662306a36Sopenharmony_ci {PARM_BYTE_LO, 0, 0xff, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 freq */ 6762306a36Sopenharmony_ci {PARM_SIGN_HI, -128, 127, parm_offset(fm2frq2), SNDRV_EMUX_UPDATE_FM2FRQ2}, /* lfo2 pitch */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci {PARM_WORD, 0, 0xffff, -1, SNDRV_EMUX_UPDATE_PITCH}, /* initial pitch */ 7062306a36Sopenharmony_ci {PARM_BYTE, 0, 0xff, parm_offset(chorus), 0}, /* chorus */ 7162306a36Sopenharmony_ci {PARM_BYTE, 0, 0xff, parm_offset(reverb), 0}, /* reverb */ 7262306a36Sopenharmony_ci {PARM_BYTE, 0, 0xff, parm_offset(cutoff), SNDRV_EMUX_UPDATE_VOLUME}, /* cutoff */ 7362306a36Sopenharmony_ci {PARM_BYTE, 0, 15, parm_offset(filterQ), SNDRV_EMUX_UPDATE_Q}, /* resonance */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci {PARM_WORD, 0, 0xffff, -1, 0}, /* sample start */ 7662306a36Sopenharmony_ci {PARM_WORD, 0, 0xffff, -1, 0}, /* loop start */ 7762306a36Sopenharmony_ci {PARM_WORD, 0, 0xffff, -1, 0}, /* loop end */ 7862306a36Sopenharmony_ci {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse sample start */ 7962306a36Sopenharmony_ci {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop start */ 8062306a36Sopenharmony_ci {PARM_WORD, 0, 0xffff, -1, 0}, /* coarse loop end */ 8162306a36Sopenharmony_ci {PARM_BYTE, 0, 0xff, -1, SNDRV_EMUX_UPDATE_VOLUME}, /* initial attenuation */ 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* set byte effect value */ 8562306a36Sopenharmony_cistatic void 8662306a36Sopenharmony_cieffect_set_byte(unsigned char *valp, struct snd_midi_channel *chan, int type) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci short effect; 8962306a36Sopenharmony_ci struct snd_emux_effect_table *fx = chan->private; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci effect = fx->val[type]; 9262306a36Sopenharmony_ci if (fx->flag[type] == EMUX_FX_FLAG_ADD) { 9362306a36Sopenharmony_ci if (parm_defs[type].type & PARM_IS_SIGNED) 9462306a36Sopenharmony_ci effect += *(char*)valp; 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci effect += *valp; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci if (effect < parm_defs[type].low) 9962306a36Sopenharmony_ci effect = parm_defs[type].low; 10062306a36Sopenharmony_ci else if (effect > parm_defs[type].high) 10162306a36Sopenharmony_ci effect = parm_defs[type].high; 10262306a36Sopenharmony_ci *valp = (unsigned char)effect; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* set word effect value */ 10662306a36Sopenharmony_cistatic void 10762306a36Sopenharmony_cieffect_set_word(unsigned short *valp, struct snd_midi_channel *chan, int type) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci int effect; 11062306a36Sopenharmony_ci struct snd_emux_effect_table *fx = chan->private; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci effect = *(unsigned short*)&fx->val[type]; 11362306a36Sopenharmony_ci if (fx->flag[type] == EMUX_FX_FLAG_ADD) 11462306a36Sopenharmony_ci effect += *valp; 11562306a36Sopenharmony_ci if (effect < parm_defs[type].low) 11662306a36Sopenharmony_ci effect = parm_defs[type].low; 11762306a36Sopenharmony_ci else if (effect > parm_defs[type].high) 11862306a36Sopenharmony_ci effect = parm_defs[type].high; 11962306a36Sopenharmony_ci *valp = (unsigned short)effect; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* address offset */ 12362306a36Sopenharmony_cistatic int 12462306a36Sopenharmony_cieffect_get_offset(struct snd_midi_channel *chan, int lo, int hi, int mode) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci int addr = 0; 12762306a36Sopenharmony_ci struct snd_emux_effect_table *fx = chan->private; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (fx->flag[hi]) 13062306a36Sopenharmony_ci addr = (short)fx->val[hi]; 13162306a36Sopenharmony_ci addr = addr << 15; 13262306a36Sopenharmony_ci if (fx->flag[lo]) 13362306a36Sopenharmony_ci addr += (short)fx->val[lo]; 13462306a36Sopenharmony_ci if (!(mode & SNDRV_SFNT_SAMPLE_8BITS)) 13562306a36Sopenharmony_ci addr /= 2; 13662306a36Sopenharmony_ci return addr; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) 14062306a36Sopenharmony_ci/* change effects - for OSS sequencer compatibility */ 14162306a36Sopenharmony_civoid 14262306a36Sopenharmony_cisnd_emux_send_effect_oss(struct snd_emux_port *port, 14362306a36Sopenharmony_ci struct snd_midi_channel *chan, int type, int val) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci int mode; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (type & 0x40) 14862306a36Sopenharmony_ci mode = EMUX_FX_FLAG_OFF; 14962306a36Sopenharmony_ci else if (type & 0x80) 15062306a36Sopenharmony_ci mode = EMUX_FX_FLAG_ADD; 15162306a36Sopenharmony_ci else 15262306a36Sopenharmony_ci mode = EMUX_FX_FLAG_SET; 15362306a36Sopenharmony_ci type &= 0x3f; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci snd_emux_send_effect(port, chan, type, val, mode); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci#endif 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* Modify the effect value. 16062306a36Sopenharmony_ci * if update is necessary, call emu8000_control 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_civoid 16362306a36Sopenharmony_cisnd_emux_send_effect(struct snd_emux_port *port, struct snd_midi_channel *chan, 16462306a36Sopenharmony_ci int type, int val, int mode) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci int i; 16762306a36Sopenharmony_ci int offset; 16862306a36Sopenharmony_ci unsigned char *srcp, *origp; 16962306a36Sopenharmony_ci struct snd_emux *emu; 17062306a36Sopenharmony_ci struct snd_emux_effect_table *fx; 17162306a36Sopenharmony_ci unsigned long flags; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci emu = port->emu; 17462306a36Sopenharmony_ci fx = chan->private; 17562306a36Sopenharmony_ci if (emu == NULL || fx == NULL) 17662306a36Sopenharmony_ci return; 17762306a36Sopenharmony_ci if (type < 0 || type >= EMUX_NUM_EFFECTS) 17862306a36Sopenharmony_ci return; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci fx->val[type] = val; 18162306a36Sopenharmony_ci fx->flag[type] = mode; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* do we need to modify the register in realtime ? */ 18462306a36Sopenharmony_ci if (!parm_defs[type].update) 18562306a36Sopenharmony_ci return; 18662306a36Sopenharmony_ci offset = parm_defs[type].offset; 18762306a36Sopenharmony_ci if (offset < 0) 18862306a36Sopenharmony_ci return; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci#ifdef SNDRV_LITTLE_ENDIAN 19162306a36Sopenharmony_ci if (parm_defs[type].type & PARM_IS_ALIGN_HI) 19262306a36Sopenharmony_ci offset++; 19362306a36Sopenharmony_ci#else 19462306a36Sopenharmony_ci if (parm_defs[type].type & PARM_IS_ALIGN_LO) 19562306a36Sopenharmony_ci offset++; 19662306a36Sopenharmony_ci#endif 19762306a36Sopenharmony_ci /* modify the register values */ 19862306a36Sopenharmony_ci spin_lock_irqsave(&emu->voice_lock, flags); 19962306a36Sopenharmony_ci for (i = 0; i < emu->max_voices; i++) { 20062306a36Sopenharmony_ci struct snd_emux_voice *vp = &emu->voices[i]; 20162306a36Sopenharmony_ci if (!STATE_IS_PLAYING(vp->state) || vp->chan != chan) 20262306a36Sopenharmony_ci continue; 20362306a36Sopenharmony_ci srcp = (unsigned char*)&vp->reg.parm + offset; 20462306a36Sopenharmony_ci origp = (unsigned char*)&vp->zone->v.parm + offset; 20562306a36Sopenharmony_ci if (parm_defs[i].type & PARM_IS_BYTE) { 20662306a36Sopenharmony_ci *srcp = *origp; 20762306a36Sopenharmony_ci effect_set_byte(srcp, chan, type); 20862306a36Sopenharmony_ci } else { 20962306a36Sopenharmony_ci *(unsigned short*)srcp = *(unsigned short*)origp; 21062306a36Sopenharmony_ci effect_set_word((unsigned short*)srcp, chan, type); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->voice_lock, flags); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* activate them */ 21662306a36Sopenharmony_ci snd_emux_update_channel(port, chan, parm_defs[type].update); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* copy wavetable registers to voice table */ 22162306a36Sopenharmony_civoid 22262306a36Sopenharmony_cisnd_emux_setup_effect(struct snd_emux_voice *vp) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct snd_midi_channel *chan = vp->chan; 22562306a36Sopenharmony_ci struct snd_emux_effect_table *fx; 22662306a36Sopenharmony_ci unsigned char *srcp; 22762306a36Sopenharmony_ci int i; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci fx = chan->private; 23062306a36Sopenharmony_ci if (!fx) 23162306a36Sopenharmony_ci return; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* modify the register values via effect table */ 23462306a36Sopenharmony_ci for (i = 0; i < EMUX_FX_END; i++) { 23562306a36Sopenharmony_ci int offset; 23662306a36Sopenharmony_ci if (!fx->flag[i]) 23762306a36Sopenharmony_ci continue; 23862306a36Sopenharmony_ci offset = parm_defs[i].offset; 23962306a36Sopenharmony_ci if (offset < 0) 24062306a36Sopenharmony_ci continue; 24162306a36Sopenharmony_ci#ifdef SNDRV_LITTLE_ENDIAN 24262306a36Sopenharmony_ci if (parm_defs[i].type & PARM_IS_ALIGN_HI) 24362306a36Sopenharmony_ci offset++; 24462306a36Sopenharmony_ci#else 24562306a36Sopenharmony_ci if (parm_defs[i].type & PARM_IS_ALIGN_LO) 24662306a36Sopenharmony_ci offset++; 24762306a36Sopenharmony_ci#endif 24862306a36Sopenharmony_ci srcp = (unsigned char*)&vp->reg.parm + offset; 24962306a36Sopenharmony_ci if (parm_defs[i].type & PARM_IS_BYTE) 25062306a36Sopenharmony_ci effect_set_byte(srcp, chan, i); 25162306a36Sopenharmony_ci else 25262306a36Sopenharmony_ci effect_set_word((unsigned short*)srcp, chan, i); 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* correct sample and loop points */ 25662306a36Sopenharmony_ci vp->reg.start += effect_get_offset(chan, EMUX_FX_SAMPLE_START, 25762306a36Sopenharmony_ci EMUX_FX_COARSE_SAMPLE_START, 25862306a36Sopenharmony_ci vp->reg.sample_mode); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci vp->reg.loopstart += effect_get_offset(chan, EMUX_FX_LOOP_START, 26162306a36Sopenharmony_ci EMUX_FX_COARSE_LOOP_START, 26262306a36Sopenharmony_ci vp->reg.sample_mode); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci vp->reg.loopend += effect_get_offset(chan, EMUX_FX_LOOP_END, 26562306a36Sopenharmony_ci EMUX_FX_COARSE_LOOP_END, 26662306a36Sopenharmony_ci vp->reg.sample_mode); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* 27062306a36Sopenharmony_ci * effect table 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_civoid 27362306a36Sopenharmony_cisnd_emux_create_effect(struct snd_emux_port *p) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci int i; 27662306a36Sopenharmony_ci p->effect = kcalloc(p->chset.max_channels, 27762306a36Sopenharmony_ci sizeof(struct snd_emux_effect_table), GFP_KERNEL); 27862306a36Sopenharmony_ci if (p->effect) { 27962306a36Sopenharmony_ci for (i = 0; i < p->chset.max_channels; i++) 28062306a36Sopenharmony_ci p->chset.channels[i].private = p->effect + i; 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci for (i = 0; i < p->chset.max_channels; i++) 28362306a36Sopenharmony_ci p->chset.channels[i].private = NULL; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_civoid 28862306a36Sopenharmony_cisnd_emux_delete_effect(struct snd_emux_port *p) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci kfree(p->effect); 29162306a36Sopenharmony_ci p->effect = NULL; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_civoid 29562306a36Sopenharmony_cisnd_emux_clear_effect(struct snd_emux_port *p) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci if (p->effect) { 29862306a36Sopenharmony_ci memset(p->effect, 0, sizeof(struct snd_emux_effect_table) * 29962306a36Sopenharmony_ci p->chset.max_channels); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci#endif /* SNDRV_EMUX_USE_RAW_EFFECT */ 304