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