brintos

brintos / linux-shallow public Read only

0
0
Text · 12.1 KiB · ef26e4d Raw
523 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  synth callback routines for Emu10k14 *5 *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>6 */7 8#include <linux/export.h>9#include "emu10k1_synth_local.h"10#include <sound/asoundef.h>11 12/* voice status */13enum {14	V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END15};16 17/* Keeps track of what we are finding */18struct best_voice {19	unsigned int time;20	int voice;21};22 23/*24 * prototypes25 */26static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw,27			  struct best_voice *best, int active_only);28static struct snd_emux_voice *get_voice(struct snd_emux *emux,29					struct snd_emux_port *port);30static int start_voice(struct snd_emux_voice *vp);31static void trigger_voice(struct snd_emux_voice *vp);32static void release_voice(struct snd_emux_voice *vp);33static void update_voice(struct snd_emux_voice *vp, int update);34static void terminate_voice(struct snd_emux_voice *vp);35static void free_voice(struct snd_emux_voice *vp);36static u32 make_fmmod(struct snd_emux_voice *vp);37static u32 make_fm2frq2(struct snd_emux_voice *vp);38static int get_pitch_shift(struct snd_emux *emu);39 40/*41 * Ensure a value is between two points42 * macro evaluates its args more than once, so changed to upper-case.43 */44#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)45#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)46 47 48/*49 * set up operators50 */51static const struct snd_emux_operators emu10k1_ops = {52	.owner =	THIS_MODULE,53	.get_voice =	get_voice,54	.prepare =	start_voice,55	.trigger =	trigger_voice,56	.release =	release_voice,57	.update =	update_voice,58	.terminate =	terminate_voice,59	.free_voice =	free_voice,60	.sample_new =	snd_emu10k1_sample_new,61	.sample_free =	snd_emu10k1_sample_free,62	.get_pitch_shift = get_pitch_shift,63};64 65void66snd_emu10k1_ops_setup(struct snd_emux *emux)67{68	emux->ops = emu10k1_ops;69}70 71 72/*73 * get more voice for pcm74 *75 * terminate most inactive voice and give it as a pcm voice.76 *77 * voice_lock is already held.78 */79int80snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)81{82	struct snd_emux *emu;83	struct snd_emux_voice *vp;84	struct best_voice best[V_END];85	int i;86 87	emu = hw->synth;88 89	lookup_voices(emu, hw, best, 1); /* no OFF voices */90	for (i = 0; i < V_END; i++) {91		if (best[i].voice >= 0) {92			int ch;93			vp = &emu->voices[best[i].voice];94			ch = vp->ch;95			if (ch < 0) {96				/*97				dev_warn(emu->card->dev,98				       "synth_get_voice: ch < 0 (%d) ??", i);99				*/100				continue;101			}102			vp->emu->num_voices--;103			vp->ch = -1;104			vp->state = SNDRV_EMUX_ST_OFF;105			return ch;106		}107	}108 109	/* not found */110	return -ENOMEM;111}112 113 114/*115 * turn off the voice (not terminated)116 */117static void118release_voice(struct snd_emux_voice *vp)119{120	struct snd_emu10k1 *hw;121	122	hw = vp->hw;123	snd_emu10k1_ptr_write_multiple(hw, vp->ch,124		DCYSUSM, (unsigned char)vp->reg.parm.modrelease | DCYSUSM_PHASE1_MASK,125		DCYSUSV, (unsigned char)vp->reg.parm.volrelease | DCYSUSV_PHASE1_MASK | DCYSUSV_CHANNELENABLE_MASK,126		REGLIST_END);127}128 129 130/*131 * terminate the voice132 */133static void134terminate_voice(struct snd_emux_voice *vp)135{136	struct snd_emu10k1 *hw;137	138	if (snd_BUG_ON(!vp))139		return;140	hw = vp->hw;141	snd_emu10k1_ptr_write_multiple(hw, vp->ch,142		DCYSUSV, 0,143		VTFT, VTFT_FILTERTARGET_MASK,144		CVCF, CVCF_CURRENTFILTER_MASK,145		PTRX, 0,146		CPF, 0,147		REGLIST_END);148	if (vp->block) {149		struct snd_emu10k1_memblk *emem;150		emem = (struct snd_emu10k1_memblk *)vp->block;151		if (emem->map_locked > 0)152			emem->map_locked--;153	}154}155 156/*157 * release the voice to system158 */159static void160free_voice(struct snd_emux_voice *vp)161{162	struct snd_emu10k1 *hw;163	164	hw = vp->hw;165	/* FIXME: emu10k1_synth is broken. */166	/* This can get called with hw == 0 */167	/* Problem apparent on plug, unplug then plug */168	/* on the Audigy 2 ZS Notebook. */169	if (hw && (vp->ch >= 0)) {170		snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);171		vp->emu->num_voices--;172		vp->ch = -1;173	}174}175 176 177/*178 * update registers179 */180static void181update_voice(struct snd_emux_voice *vp, int update)182{183	struct snd_emu10k1 *hw;184	185	hw = vp->hw;186	if (update & SNDRV_EMUX_UPDATE_VOLUME)187		snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol);188	if (update & SNDRV_EMUX_UPDATE_PITCH)189		snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);190	if (update & SNDRV_EMUX_UPDATE_PAN) {191		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan);192		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);193	}194	if (update & SNDRV_EMUX_UPDATE_FMMOD)195		snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, make_fmmod(vp));196	if (update & SNDRV_EMUX_UPDATE_TREMFREQ)197		snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);198	if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)199		snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, make_fm2frq2(vp));200	if (update & SNDRV_EMUX_UPDATE_Q)201		snd_emu10k1_ptr_write(hw, CCCA_RESONANCE, vp->ch, vp->reg.parm.filterQ);202}203 204 205/*206 * look up voice table - get the best voice in order of preference207 */208/* spinlock held! */209static void210lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,211	      struct best_voice *best, int active_only)212{213	struct snd_emux_voice *vp;214	struct best_voice *bp;215	int  i;216 217	for (i = 0; i < V_END; i++) {218		best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */219		best[i].voice = -1;220	}221 222	/*223	 * Go through them all and get a best one to use.224	 * NOTE: could also look at volume and pick the quietest one.225	 */226	for (i = 0; i < emu->max_voices; i++) {227		int state, val;228 229		vp = &emu->voices[i];230		state = vp->state;231		if (state == SNDRV_EMUX_ST_OFF) {232			if (vp->ch < 0) {233				if (active_only)234					continue;235				bp = best + V_FREE;236			} else237				bp = best + V_OFF;238		}239		else if (state == SNDRV_EMUX_ST_RELEASED ||240			 state == SNDRV_EMUX_ST_PENDING) {241			bp = best + V_RELEASED;242#if 1243			val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch);244			if (! val)245				bp = best + V_OFF;246#endif247		}248		else if (state == SNDRV_EMUX_ST_STANDBY)249			continue;250		else if (state & SNDRV_EMUX_ST_ON)251			bp = best + V_PLAYING;252		else253			continue;254 255		/* check if sample is finished playing (non-looping only) */256		if (bp != best + V_OFF && bp != best + V_FREE &&257		    (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {258			val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch) - 64 + 3;259			if (val >= vp->reg.loopstart)260				bp = best + V_OFF;261		}262 263		if (vp->time < bp->time) {264			bp->time = vp->time;265			bp->voice = i;266		}267	}268}269 270/*271 * get an empty voice272 *273 * emu->voice_lock is already held.274 */275static struct snd_emux_voice *276get_voice(struct snd_emux *emu, struct snd_emux_port *port)277{278	struct snd_emu10k1 *hw;279	struct snd_emux_voice *vp;280	struct best_voice best[V_END];281	int i;282 283	hw = emu->hw;284 285	lookup_voices(emu, hw, best, 0);286	for (i = 0; i < V_END; i++) {287		if (best[i].voice >= 0) {288			vp = &emu->voices[best[i].voice];289			if (vp->ch < 0) {290				/* allocate a voice */291				struct snd_emu10k1_voice *hwvoice;292				if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, 1, NULL, &hwvoice) < 0)293					continue;294				vp->ch = hwvoice->number;295				emu->num_voices++;296			}297			return vp;298		}299	}300 301	/* not found */302	return NULL;303}304 305/*306 * prepare envelopes and LFOs307 */308static int309start_voice(struct snd_emux_voice *vp)310{311	unsigned int temp;312	int ch;313	bool w_16;314	u32 psst, dsl, map, ccca, vtarget;315	unsigned int addr, mapped_offset;316	struct snd_midi_channel *chan;317	struct snd_emu10k1 *hw;318	struct snd_emu10k1_memblk *emem;319	320	hw = vp->hw;321	ch = vp->ch;322	if (snd_BUG_ON(ch < 0))323		return -EINVAL;324	chan = vp->chan;325	w_16 = !(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS);326 327	emem = (struct snd_emu10k1_memblk *)vp->block;328	if (emem == NULL)329		return -EINVAL;330	emem->map_locked++;331	if (snd_emu10k1_memblk_map(hw, emem) < 0) {332		/* dev_err(hw->card->devK, "emu: cannot map!\n"); */333		return -ENOMEM;334	}335	mapped_offset = snd_emu10k1_memblk_offset(emem) >> w_16;336	vp->reg.start += mapped_offset;337	vp->reg.end += mapped_offset;338	vp->reg.loopstart += mapped_offset;339	vp->reg.loopend += mapped_offset;340 341	/* set channel routing */342	/* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */343	if (hw->audigy) {344		temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | 345			(FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24);346		snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp);347	} else {348		temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | 349			(FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28);350		snd_emu10k1_ptr_write(hw, FXRT, ch, temp);351	}352 353	temp = vp->reg.parm.reverb;354	temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;355	LIMITMAX(temp, 255);356	addr = vp->reg.loopstart;357	psst = (temp << 24) | addr;358 359	addr = vp->reg.loopend;360	temp = vp->reg.parm.chorus;361	temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;362	LIMITMAX(temp, 255);363	dsl = (temp << 24) | addr;364 365	map = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);366 367	addr = vp->reg.start + 64 - 3;368	temp = vp->reg.parm.filterQ;369	ccca = (temp << 28) | addr;370	if (vp->apitch < 0xe400)371		ccca |= CCCA_INTERPROM_0;372	else {373		unsigned int shift = (vp->apitch - 0xe000) >> 10;374		ccca |= shift << 25;375	}376	if (!w_16)377		ccca |= CCCA_8BITSELECT;378 379	vtarget = (unsigned int)vp->vtarget << 16;380 381	snd_emu10k1_ptr_write_multiple(hw, ch,382		/* channel to be silent and idle */383		DCYSUSV, 0,384		VTFT, VTFT_FILTERTARGET_MASK,385		CVCF, CVCF_CURRENTFILTER_MASK,386		PTRX, 0,387		CPF, 0,388 389		/* set pitch offset */390		IP, vp->apitch,391 392		/* set envelope parameters */393		ENVVAL, vp->reg.parm.moddelay,394		ATKHLDM, vp->reg.parm.modatkhld,395		DCYSUSM, vp->reg.parm.moddcysus,396		ENVVOL, vp->reg.parm.voldelay,397		ATKHLDV, vp->reg.parm.volatkhld,398		/* decay/sustain parameter for volume envelope is used399		   for triggerg the voice */400 401		/* cutoff and volume */402		IFATN, (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol,403 404		/* modulation envelope heights */405		PEFE, vp->reg.parm.pefe,406 407		/* lfo1/2 delay */408		LFOVAL1, vp->reg.parm.lfo1delay,409		LFOVAL2, vp->reg.parm.lfo2delay,410 411		/* lfo1 pitch & cutoff shift */412		FMMOD, make_fmmod(vp),413		/* lfo1 volume & freq */414		TREMFRQ, vp->reg.parm.tremfrq,415		/* lfo2 pitch & freq */416		FM2FRQ2, make_fm2frq2(vp),417 418		/* reverb and loop start (reverb 8bit, MSB) */419		PSST, psst,420 421		/* chorus & loop end (chorus 8bit, MSB) */422		DSL, dsl,423 424		/* clear filter delay memory */425		Z1, 0,426		Z2, 0,427 428		/* invalidate maps */429		MAPA, map,430		MAPB, map,431 432		/* Q & current address (Q 4bit value, MSB) */433		CCCA, ccca,434 435		/* cache */436		CCR, REG_VAL_PUT(CCR_CACHEINVALIDSIZE, 64),437 438		/* reset volume */439		VTFT, vtarget | vp->ftarget,440		CVCF, vtarget | CVCF_CURRENTFILTER_MASK,441 442		REGLIST_END);443 444	hw->voices[ch].dirty = 1;445	return 0;446}447 448/*449 * Start envelope450 */451static void452trigger_voice(struct snd_emux_voice *vp)453{454	unsigned int ptarget;455	struct snd_emu10k1 *hw;456	struct snd_emu10k1_memblk *emem;457	458	hw = vp->hw;459 460	emem = (struct snd_emu10k1_memblk *)vp->block;461	if (! emem || emem->mapped_page < 0)462		return; /* not mapped */463 464#if 0465	ptarget = (unsigned int)vp->ptarget << 16;466#else467	ptarget = IP_TO_CP(vp->apitch);468#endif469	snd_emu10k1_ptr_write_multiple(hw, vp->ch,470		/* set pitch target and pan (volume) */471		PTRX, ptarget | (vp->apan << 8) | vp->aaux,472 473		/* current pitch and fractional address */474		CPF, ptarget,475 476		/* enable envelope engine */477		DCYSUSV, vp->reg.parm.voldcysus | DCYSUSV_CHANNELENABLE_MASK,478 479		REGLIST_END);480}481 482#define MOD_SENSE 18483 484/* calculate lfo1 modulation height and cutoff register */485static u32486make_fmmod(struct snd_emux_voice *vp)487{488	short pitch;489	unsigned char cutoff;490	int modulation;491 492	pitch = (char)(vp->reg.parm.fmmod>>8);493	cutoff = (vp->reg.parm.fmmod & 0xff);494	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;495	pitch += (MOD_SENSE * modulation) / 1200;496	LIMITVALUE(pitch, -128, 127);497	return ((unsigned char)pitch << 8) | cutoff;498}499 500/* calculate set lfo2 pitch & frequency register */501static u32502make_fm2frq2(struct snd_emux_voice *vp)503{504	short pitch;505	unsigned char freq;506	int modulation;507 508	pitch = (char)(vp->reg.parm.fm2frq2>>8);509	freq = vp->reg.parm.fm2frq2 & 0xff;510	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;511	pitch += (MOD_SENSE * modulation) / 1200;512	LIMITVALUE(pitch, -128, 127);513	return ((unsigned char)pitch << 8) | freq;514}515 516static int get_pitch_shift(struct snd_emux *emu)517{518	struct snd_emu10k1 *hw = emu->hw;519 520	return (hw->card_capabilities->emu_model &&521			hw->emu1010.word_clock == 44100) ? 0 : -501;522}523