brintos

brintos / linux-shallow public Read only

0
0
Text · 12.6 KiB · 7609a5b Raw
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