xref: /openbmc/linux/sound/synth/emux/emux_synth.c (revision e68235c8)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Midi synth routines for the Emu8k/Emu10k1
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 1999 Steve Ratcliffe
61da177e4SLinus Torvalds  *  Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *  Contains code based on awe_wave.c by Takashi Iwai
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
11d81a6d71SPaul Gortmaker #include <linux/export.h>
121da177e4SLinus Torvalds #include "emux_voice.h"
131da177e4SLinus Torvalds #include <sound/asoundef.h>
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds /*
161da177e4SLinus Torvalds  * Prototypes
171da177e4SLinus Torvalds  */
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds /*
201da177e4SLinus Torvalds  * Ensure a value is between two points
211da177e4SLinus Torvalds  * macro evaluates its args more than once, so changed to upper-case.
221da177e4SLinus Torvalds  */
231da177e4SLinus Torvalds #define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
241da177e4SLinus Torvalds #define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
251da177e4SLinus Torvalds 
2603da312aSTakashi Iwai static int get_zone(struct snd_emux *emu, struct snd_emux_port *port,
2703da312aSTakashi Iwai 		    int *notep, int vel, struct snd_midi_channel *chan,
2803da312aSTakashi Iwai 		    struct snd_sf_zone **table);
2903da312aSTakashi Iwai static int get_bank(struct snd_emux_port *port, struct snd_midi_channel *chan);
3003da312aSTakashi Iwai static void terminate_note1(struct snd_emux *emu, int note,
3103da312aSTakashi Iwai 			    struct snd_midi_channel *chan, int free);
3203da312aSTakashi Iwai static void exclusive_note_off(struct snd_emux *emu, struct snd_emux_port *port,
3303da312aSTakashi Iwai 			       int exclass);
3403da312aSTakashi Iwai static void terminate_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int free);
3503da312aSTakashi Iwai static void update_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int update);
3603da312aSTakashi Iwai static void setup_voice(struct snd_emux_voice *vp);
3703da312aSTakashi Iwai static int calc_pan(struct snd_emux_voice *vp);
3803da312aSTakashi Iwai static int calc_volume(struct snd_emux_voice *vp);
3903da312aSTakashi Iwai static int calc_pitch(struct snd_emux_voice *vp);
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds /*
431da177e4SLinus Torvalds  * Start a note.
441da177e4SLinus Torvalds  */
451da177e4SLinus Torvalds void
snd_emux_note_on(void * p,int note,int vel,struct snd_midi_channel * chan)4603da312aSTakashi Iwai snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
471da177e4SLinus Torvalds {
4803da312aSTakashi Iwai 	struct snd_emux *emu;
491da177e4SLinus Torvalds 	int i, key, nvoices;
5003da312aSTakashi Iwai 	struct snd_emux_voice *vp;
5103da312aSTakashi Iwai 	struct snd_sf_zone *table[SNDRV_EMUX_MAX_MULTI_VOICES];
521da177e4SLinus Torvalds 	unsigned long flags;
5303da312aSTakashi Iwai 	struct snd_emux_port *port;
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	port = p;
565e246b85STakashi Iwai 	if (snd_BUG_ON(!port || !chan))
575e246b85STakashi Iwai 		return;
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds 	emu = port->emu;
605e246b85STakashi Iwai 	if (snd_BUG_ON(!emu || !emu->ops.get_voice || !emu->ops.trigger))
615e246b85STakashi Iwai 		return;
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds 	key = note; /* remember the original note */
641da177e4SLinus Torvalds 	nvoices = get_zone(emu, port, &note, vel, chan, table);
651da177e4SLinus Torvalds 	if (! nvoices)
661da177e4SLinus Torvalds 		return;
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	/* exclusive note off */
691da177e4SLinus Torvalds 	for (i = 0; i < nvoices; i++) {
7003da312aSTakashi Iwai 		struct snd_sf_zone *zp = table[i];
711da177e4SLinus Torvalds 		if (zp && zp->v.exclusiveClass)
721da177e4SLinus Torvalds 			exclusive_note_off(emu, port, zp->v.exclusiveClass);
731da177e4SLinus Torvalds 	}
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds #if 0 // seems not necessary
761da177e4SLinus Torvalds 	/* Turn off the same note on the same channel. */
771da177e4SLinus Torvalds 	terminate_note1(emu, key, chan, 0);
781da177e4SLinus Torvalds #endif
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
811da177e4SLinus Torvalds 	for (i = 0; i < nvoices; i++) {
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 		/* set up each voice parameter */
841da177e4SLinus Torvalds 		/* at this stage, we don't trigger the voice yet. */
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 		if (table[i] == NULL)
871da177e4SLinus Torvalds 			continue;
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 		vp = emu->ops.get_voice(emu, port);
901da177e4SLinus Torvalds 		if (vp == NULL || vp->ch < 0)
911da177e4SLinus Torvalds 			continue;
921da177e4SLinus Torvalds 		if (STATE_IS_PLAYING(vp->state))
931da177e4SLinus Torvalds 			emu->ops.terminate(vp);
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 		vp->time = emu->use_time++;
961da177e4SLinus Torvalds 		vp->chan = chan;
971da177e4SLinus Torvalds 		vp->port = port;
981da177e4SLinus Torvalds 		vp->key = key;
991da177e4SLinus Torvalds 		vp->note = note;
1001da177e4SLinus Torvalds 		vp->velocity = vel;
1011da177e4SLinus Torvalds 		vp->zone = table[i];
1021da177e4SLinus Torvalds 		if (vp->zone->sample)
1031da177e4SLinus Torvalds 			vp->block = vp->zone->sample->block;
1041da177e4SLinus Torvalds 		else
1051da177e4SLinus Torvalds 			vp->block = NULL;
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 		setup_voice(vp);
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 		vp->state = SNDRV_EMUX_ST_STANDBY;
1101da177e4SLinus Torvalds 		if (emu->ops.prepare) {
1111da177e4SLinus Torvalds 			vp->state = SNDRV_EMUX_ST_OFF;
1121da177e4SLinus Torvalds 			if (emu->ops.prepare(vp) >= 0)
1131da177e4SLinus Torvalds 				vp->state = SNDRV_EMUX_ST_STANDBY;
1141da177e4SLinus Torvalds 		}
1151da177e4SLinus Torvalds 	}
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 	/* start envelope now */
1181da177e4SLinus Torvalds 	for (i = 0; i < emu->max_voices; i++) {
1191da177e4SLinus Torvalds 		vp = &emu->voices[i];
1201da177e4SLinus Torvalds 		if (vp->state == SNDRV_EMUX_ST_STANDBY &&
1211da177e4SLinus Torvalds 		    vp->chan == chan) {
1221da177e4SLinus Torvalds 			emu->ops.trigger(vp);
1231da177e4SLinus Torvalds 			vp->state = SNDRV_EMUX_ST_ON;
1241da177e4SLinus Torvalds 			vp->ontime = jiffies; /* remember the trigger timing */
1251da177e4SLinus Torvalds 		}
1261da177e4SLinus Torvalds 	}
1271da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds #ifdef SNDRV_EMUX_USE_RAW_EFFECT
1301da177e4SLinus Torvalds 	if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) {
1311da177e4SLinus Torvalds 		/* clear voice position for the next note on this channel */
13203da312aSTakashi Iwai 		struct snd_emux_effect_table *fx = chan->private;
1331da177e4SLinus Torvalds 		if (fx) {
1341da177e4SLinus Torvalds 			fx->flag[EMUX_FX_SAMPLE_START] = 0;
1351da177e4SLinus Torvalds 			fx->flag[EMUX_FX_COARSE_SAMPLE_START] = 0;
1361da177e4SLinus Torvalds 		}
1371da177e4SLinus Torvalds 	}
1381da177e4SLinus Torvalds #endif
1391da177e4SLinus Torvalds }
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds /*
1421da177e4SLinus Torvalds  * Release a note in response to a midi note off.
1431da177e4SLinus Torvalds  */
1441da177e4SLinus Torvalds void
snd_emux_note_off(void * p,int note,int vel,struct snd_midi_channel * chan)14503da312aSTakashi Iwai snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan)
1461da177e4SLinus Torvalds {
1471da177e4SLinus Torvalds 	int ch;
14803da312aSTakashi Iwai 	struct snd_emux *emu;
14903da312aSTakashi Iwai 	struct snd_emux_voice *vp;
1501da177e4SLinus Torvalds 	unsigned long flags;
15103da312aSTakashi Iwai 	struct snd_emux_port *port;
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 	port = p;
1545e246b85STakashi Iwai 	if (snd_BUG_ON(!port || !chan))
1555e246b85STakashi Iwai 		return;
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 	emu = port->emu;
1585e246b85STakashi Iwai 	if (snd_BUG_ON(!emu || !emu->ops.release))
1595e246b85STakashi Iwai 		return;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
1621da177e4SLinus Torvalds 	for (ch = 0; ch < emu->max_voices; ch++) {
1631da177e4SLinus Torvalds 		vp = &emu->voices[ch];
1641da177e4SLinus Torvalds 		if (STATE_IS_PLAYING(vp->state) &&
1651da177e4SLinus Torvalds 		    vp->chan == chan && vp->key == note) {
1661da177e4SLinus Torvalds 			vp->state = SNDRV_EMUX_ST_RELEASED;
1671da177e4SLinus Torvalds 			if (vp->ontime == jiffies) {
1681da177e4SLinus Torvalds 				/* if note-off is sent too shortly after
1691da177e4SLinus Torvalds 				 * note-on, emuX engine cannot produce the sound
1701da177e4SLinus Torvalds 				 * correctly.  so we'll release this note
1711da177e4SLinus Torvalds 				 * a bit later via timer callback.
1721da177e4SLinus Torvalds 				 */
1731da177e4SLinus Torvalds 				vp->state = SNDRV_EMUX_ST_PENDING;
1741da177e4SLinus Torvalds 				if (! emu->timer_active) {
175abd08352STakashi Iwai 					mod_timer(&emu->tlist, jiffies + 1);
1761da177e4SLinus Torvalds 					emu->timer_active = 1;
1771da177e4SLinus Torvalds 				}
1781da177e4SLinus Torvalds 			} else
1791da177e4SLinus Torvalds 				/* ok now release the note */
1801da177e4SLinus Torvalds 				emu->ops.release(vp);
1811da177e4SLinus Torvalds 		}
1821da177e4SLinus Torvalds 	}
1831da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
1841da177e4SLinus Torvalds }
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds /*
1871da177e4SLinus Torvalds  * timer callback
1881da177e4SLinus Torvalds  *
1891da177e4SLinus Torvalds  * release the pending note-offs
1901da177e4SLinus Torvalds  */
snd_emux_timer_callback(struct timer_list * t)19108352b20SKees Cook void snd_emux_timer_callback(struct timer_list *t)
1921da177e4SLinus Torvalds {
19308352b20SKees Cook 	struct snd_emux *emu = from_timer(emu, t, tlist);
19403da312aSTakashi Iwai 	struct snd_emux_voice *vp;
195b32425acSTakashi Iwai 	unsigned long flags;
1961da177e4SLinus Torvalds 	int ch, do_again = 0;
1971da177e4SLinus Torvalds 
198b32425acSTakashi Iwai 	spin_lock_irqsave(&emu->voice_lock, flags);
1991da177e4SLinus Torvalds 	for (ch = 0; ch < emu->max_voices; ch++) {
2001da177e4SLinus Torvalds 		vp = &emu->voices[ch];
2011da177e4SLinus Torvalds 		if (vp->state == SNDRV_EMUX_ST_PENDING) {
2021da177e4SLinus Torvalds 			if (vp->ontime == jiffies)
2031da177e4SLinus Torvalds 				do_again++; /* release this at the next interrupt */
2041da177e4SLinus Torvalds 			else {
2051da177e4SLinus Torvalds 				emu->ops.release(vp);
2061da177e4SLinus Torvalds 				vp->state = SNDRV_EMUX_ST_RELEASED;
2071da177e4SLinus Torvalds 			}
2081da177e4SLinus Torvalds 		}
2091da177e4SLinus Torvalds 	}
2101da177e4SLinus Torvalds 	if (do_again) {
211abd08352STakashi Iwai 		mod_timer(&emu->tlist, jiffies + 1);
2121da177e4SLinus Torvalds 		emu->timer_active = 1;
2131da177e4SLinus Torvalds 	} else
2141da177e4SLinus Torvalds 		emu->timer_active = 0;
215b32425acSTakashi Iwai 	spin_unlock_irqrestore(&emu->voice_lock, flags);
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds /*
2191da177e4SLinus Torvalds  * key pressure change
2201da177e4SLinus Torvalds  */
2211da177e4SLinus Torvalds void
snd_emux_key_press(void * p,int note,int vel,struct snd_midi_channel * chan)22203da312aSTakashi Iwai snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
2231da177e4SLinus Torvalds {
2241da177e4SLinus Torvalds 	int ch;
22503da312aSTakashi Iwai 	struct snd_emux *emu;
22603da312aSTakashi Iwai 	struct snd_emux_voice *vp;
2271da177e4SLinus Torvalds 	unsigned long flags;
22803da312aSTakashi Iwai 	struct snd_emux_port *port;
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds 	port = p;
2315e246b85STakashi Iwai 	if (snd_BUG_ON(!port || !chan))
2325e246b85STakashi Iwai 		return;
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 	emu = port->emu;
2355e246b85STakashi Iwai 	if (snd_BUG_ON(!emu || !emu->ops.update))
2365e246b85STakashi Iwai 		return;
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
2391da177e4SLinus Torvalds 	for (ch = 0; ch < emu->max_voices; ch++) {
2401da177e4SLinus Torvalds 		vp = &emu->voices[ch];
2411da177e4SLinus Torvalds 		if (vp->state == SNDRV_EMUX_ST_ON &&
2421da177e4SLinus Torvalds 		    vp->chan == chan && vp->key == note) {
2431da177e4SLinus Torvalds 			vp->velocity = vel;
2441da177e4SLinus Torvalds 			update_voice(emu, vp, SNDRV_EMUX_UPDATE_VOLUME);
2451da177e4SLinus Torvalds 		}
2461da177e4SLinus Torvalds 	}
2471da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
2481da177e4SLinus Torvalds }
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds /*
2521da177e4SLinus Torvalds  * Modulate the voices which belong to the channel
2531da177e4SLinus Torvalds  */
2541da177e4SLinus Torvalds void
snd_emux_update_channel(struct snd_emux_port * port,struct snd_midi_channel * chan,int update)25503da312aSTakashi Iwai snd_emux_update_channel(struct snd_emux_port *port, struct snd_midi_channel *chan, int update)
2561da177e4SLinus Torvalds {
25703da312aSTakashi Iwai 	struct snd_emux *emu;
25803da312aSTakashi Iwai 	struct snd_emux_voice *vp;
2591da177e4SLinus Torvalds 	int i;
2601da177e4SLinus Torvalds 	unsigned long flags;
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds 	if (! update)
2631da177e4SLinus Torvalds 		return;
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds 	emu = port->emu;
2665e246b85STakashi Iwai 	if (snd_BUG_ON(!emu || !emu->ops.update))
2675e246b85STakashi Iwai 		return;
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
2701da177e4SLinus Torvalds 	for (i = 0; i < emu->max_voices; i++) {
2711da177e4SLinus Torvalds 		vp = &emu->voices[i];
2721da177e4SLinus Torvalds 		if (vp->chan == chan)
2731da177e4SLinus Torvalds 			update_voice(emu, vp, update);
2741da177e4SLinus Torvalds 	}
2751da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
2761da177e4SLinus Torvalds }
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds /*
2791da177e4SLinus Torvalds  * Modulate all the voices which belong to the port.
2801da177e4SLinus Torvalds  */
2811da177e4SLinus Torvalds void
snd_emux_update_port(struct snd_emux_port * port,int update)28203da312aSTakashi Iwai snd_emux_update_port(struct snd_emux_port *port, int update)
2831da177e4SLinus Torvalds {
28403da312aSTakashi Iwai 	struct snd_emux *emu;
28503da312aSTakashi Iwai 	struct snd_emux_voice *vp;
2861da177e4SLinus Torvalds 	int i;
2871da177e4SLinus Torvalds 	unsigned long flags;
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds 	if (! update)
2901da177e4SLinus Torvalds 		return;
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	emu = port->emu;
2935e246b85STakashi Iwai 	if (snd_BUG_ON(!emu || !emu->ops.update))
2945e246b85STakashi Iwai 		return;
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
2971da177e4SLinus Torvalds 	for (i = 0; i < emu->max_voices; i++) {
2981da177e4SLinus Torvalds 		vp = &emu->voices[i];
2991da177e4SLinus Torvalds 		if (vp->port == port)
3001da177e4SLinus Torvalds 			update_voice(emu, vp, update);
3011da177e4SLinus Torvalds 	}
3021da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
3031da177e4SLinus Torvalds }
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds /*
3073a4fa0a2SRobert P. J. Day  * Deal with a controller type event.  This includes all types of
3081da177e4SLinus Torvalds  * control events, not just the midi controllers
3091da177e4SLinus Torvalds  */
3101da177e4SLinus Torvalds void
snd_emux_control(void * p,int type,struct snd_midi_channel * chan)31103da312aSTakashi Iwai snd_emux_control(void *p, int type, struct snd_midi_channel *chan)
3121da177e4SLinus Torvalds {
31303da312aSTakashi Iwai 	struct snd_emux_port *port;
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	port = p;
3165e246b85STakashi Iwai 	if (snd_BUG_ON(!port || !chan))
3175e246b85STakashi Iwai 		return;
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	switch (type) {
3201da177e4SLinus Torvalds 	case MIDI_CTL_MSB_MAIN_VOLUME:
3211da177e4SLinus Torvalds 	case MIDI_CTL_MSB_EXPRESSION:
3221da177e4SLinus Torvalds 		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_VOLUME);
3231da177e4SLinus Torvalds 		break;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	case MIDI_CTL_MSB_PAN:
3261da177e4SLinus Torvalds 		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PAN);
3271da177e4SLinus Torvalds 		break;
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 	case MIDI_CTL_SOFT_PEDAL:
3301da177e4SLinus Torvalds #ifdef SNDRV_EMUX_USE_RAW_EFFECT
3311da177e4SLinus Torvalds 		/* FIXME: this is an emulation */
332bf91141dSmaximilian attems 		if (chan->control[type] >= 64)
3331da177e4SLinus Torvalds 			snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, -160,
3341da177e4SLinus Torvalds 				     EMUX_FX_FLAG_ADD);
335bf91141dSmaximilian attems 		else
336bf91141dSmaximilian attems 			snd_emux_send_effect(port, chan, EMUX_FX_CUTOFF, 0,
337bf91141dSmaximilian attems 				     EMUX_FX_FLAG_OFF);
3381da177e4SLinus Torvalds #endif
3391da177e4SLinus Torvalds 		break;
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds 	case MIDI_CTL_PITCHBEND:
3421da177e4SLinus Torvalds 		snd_emux_update_channel(port, chan, SNDRV_EMUX_UPDATE_PITCH);
3431da177e4SLinus Torvalds 		break;
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds 	case MIDI_CTL_MSB_MODWHEEL:
3461da177e4SLinus Torvalds 	case MIDI_CTL_CHAN_PRESSURE:
3471da177e4SLinus Torvalds 		snd_emux_update_channel(port, chan,
3481da177e4SLinus Torvalds 					SNDRV_EMUX_UPDATE_FMMOD |
3491da177e4SLinus Torvalds 					SNDRV_EMUX_UPDATE_FM2FRQ2);
3501da177e4SLinus Torvalds 		break;
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 	}
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds 	if (port->chset.midi_mode == SNDRV_MIDI_MODE_XG) {
3551da177e4SLinus Torvalds 		snd_emux_xg_control(port, chan, type);
3561da177e4SLinus Torvalds 	}
3571da177e4SLinus Torvalds }
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds /*
3611da177e4SLinus Torvalds  * terminate note - if free flag is true, free the terminated voice
3621da177e4SLinus Torvalds  */
3631da177e4SLinus Torvalds static void
terminate_note1(struct snd_emux * emu,int note,struct snd_midi_channel * chan,int free)36403da312aSTakashi Iwai terminate_note1(struct snd_emux *emu, int note, struct snd_midi_channel *chan, int free)
3651da177e4SLinus Torvalds {
3661da177e4SLinus Torvalds 	int  i;
36703da312aSTakashi Iwai 	struct snd_emux_voice *vp;
3681da177e4SLinus Torvalds 	unsigned long flags;
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
3711da177e4SLinus Torvalds 	for (i = 0; i < emu->max_voices; i++) {
3721da177e4SLinus Torvalds 		vp = &emu->voices[i];
3731da177e4SLinus Torvalds 		if (STATE_IS_PLAYING(vp->state) && vp->chan == chan &&
3741da177e4SLinus Torvalds 		    vp->key == note)
3751da177e4SLinus Torvalds 			terminate_voice(emu, vp, free);
3761da177e4SLinus Torvalds 	}
3771da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
3781da177e4SLinus Torvalds }
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds /*
3821da177e4SLinus Torvalds  * terminate note - exported for midi emulation
3831da177e4SLinus Torvalds  */
3841da177e4SLinus Torvalds void
snd_emux_terminate_note(void * p,int note,struct snd_midi_channel * chan)38503da312aSTakashi Iwai snd_emux_terminate_note(void *p, int note, struct snd_midi_channel *chan)
3861da177e4SLinus Torvalds {
38703da312aSTakashi Iwai 	struct snd_emux *emu;
38803da312aSTakashi Iwai 	struct snd_emux_port *port;
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds 	port = p;
3915e246b85STakashi Iwai 	if (snd_BUG_ON(!port || !chan))
3925e246b85STakashi Iwai 		return;
3931da177e4SLinus Torvalds 
3941da177e4SLinus Torvalds 	emu = port->emu;
3955e246b85STakashi Iwai 	if (snd_BUG_ON(!emu || !emu->ops.terminate))
3965e246b85STakashi Iwai 		return;
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds 	terminate_note1(emu, note, chan, 1);
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds /*
4031da177e4SLinus Torvalds  * Terminate all the notes
4041da177e4SLinus Torvalds  */
4051da177e4SLinus Torvalds void
snd_emux_terminate_all(struct snd_emux * emu)40603da312aSTakashi Iwai snd_emux_terminate_all(struct snd_emux *emu)
4071da177e4SLinus Torvalds {
4081da177e4SLinus Torvalds 	int i;
40903da312aSTakashi Iwai 	struct snd_emux_voice *vp;
4101da177e4SLinus Torvalds 	unsigned long flags;
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
4131da177e4SLinus Torvalds 	for (i = 0; i < emu->max_voices; i++) {
4141da177e4SLinus Torvalds 		vp = &emu->voices[i];
4151da177e4SLinus Torvalds 		if (STATE_IS_PLAYING(vp->state))
4161da177e4SLinus Torvalds 			terminate_voice(emu, vp, 0);
4171da177e4SLinus Torvalds 		if (vp->state == SNDRV_EMUX_ST_OFF) {
4181da177e4SLinus Torvalds 			if (emu->ops.free_voice)
4191da177e4SLinus Torvalds 				emu->ops.free_voice(vp);
4201da177e4SLinus Torvalds 			if (emu->ops.reset)
4211da177e4SLinus Torvalds 				emu->ops.reset(emu, i);
4221da177e4SLinus Torvalds 		}
4231da177e4SLinus Torvalds 		vp->time = 0;
4241da177e4SLinus Torvalds 	}
4251da177e4SLinus Torvalds 	/* initialize allocation time */
4261da177e4SLinus Torvalds 	emu->use_time = 0;
4271da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
4281da177e4SLinus Torvalds }
4291da177e4SLinus Torvalds 
43095ff1756STakashi Iwai EXPORT_SYMBOL(snd_emux_terminate_all);
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds /*
4331da177e4SLinus Torvalds  * Terminate all voices associated with the given port
4341da177e4SLinus Torvalds  */
4351da177e4SLinus Torvalds void
snd_emux_sounds_off_all(struct snd_emux_port * port)43603da312aSTakashi Iwai snd_emux_sounds_off_all(struct snd_emux_port *port)
4371da177e4SLinus Torvalds {
4381da177e4SLinus Torvalds 	int i;
43903da312aSTakashi Iwai 	struct snd_emux *emu;
44003da312aSTakashi Iwai 	struct snd_emux_voice *vp;
4411da177e4SLinus Torvalds 	unsigned long flags;
4421da177e4SLinus Torvalds 
4435e246b85STakashi Iwai 	if (snd_BUG_ON(!port))
4445e246b85STakashi Iwai 		return;
4451da177e4SLinus Torvalds 	emu = port->emu;
4465e246b85STakashi Iwai 	if (snd_BUG_ON(!emu || !emu->ops.terminate))
4475e246b85STakashi Iwai 		return;
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
4501da177e4SLinus Torvalds 	for (i = 0; i < emu->max_voices; i++) {
4511da177e4SLinus Torvalds 		vp = &emu->voices[i];
4521da177e4SLinus Torvalds 		if (STATE_IS_PLAYING(vp->state) &&
4531da177e4SLinus Torvalds 		    vp->port == port)
4541da177e4SLinus Torvalds 			terminate_voice(emu, vp, 0);
4551da177e4SLinus Torvalds 		if (vp->state == SNDRV_EMUX_ST_OFF) {
4561da177e4SLinus Torvalds 			if (emu->ops.free_voice)
4571da177e4SLinus Torvalds 				emu->ops.free_voice(vp);
4581da177e4SLinus Torvalds 			if (emu->ops.reset)
4591da177e4SLinus Torvalds 				emu->ops.reset(emu, i);
4601da177e4SLinus Torvalds 		}
4611da177e4SLinus Torvalds 	}
4621da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
4631da177e4SLinus Torvalds }
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds /*
4671da177e4SLinus Torvalds  * Terminate all voices that have the same exclusive class.  This
4681da177e4SLinus Torvalds  * is mainly for drums.
4691da177e4SLinus Torvalds  */
4701da177e4SLinus Torvalds static void
exclusive_note_off(struct snd_emux * emu,struct snd_emux_port * port,int exclass)47103da312aSTakashi Iwai exclusive_note_off(struct snd_emux *emu, struct snd_emux_port *port, int exclass)
4721da177e4SLinus Torvalds {
47303da312aSTakashi Iwai 	struct snd_emux_voice *vp;
4741da177e4SLinus Torvalds 	int  i;
4751da177e4SLinus Torvalds 	unsigned long flags;
4761da177e4SLinus Torvalds 
4771da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
4781da177e4SLinus Torvalds 	for (i = 0; i < emu->max_voices; i++) {
4791da177e4SLinus Torvalds 		vp = &emu->voices[i];
4801da177e4SLinus Torvalds 		if (STATE_IS_PLAYING(vp->state) && vp->port == port &&
4811da177e4SLinus Torvalds 		    vp->reg.exclusiveClass == exclass) {
4821da177e4SLinus Torvalds 			terminate_voice(emu, vp, 0);
4831da177e4SLinus Torvalds 		}
4841da177e4SLinus Torvalds 	}
4851da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
4861da177e4SLinus Torvalds }
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds /*
4891da177e4SLinus Torvalds  * terminate a voice
4901da177e4SLinus Torvalds  * if free flag is true, call free_voice after termination
4911da177e4SLinus Torvalds  */
4921da177e4SLinus Torvalds static void
terminate_voice(struct snd_emux * emu,struct snd_emux_voice * vp,int free)49303da312aSTakashi Iwai terminate_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int free)
4941da177e4SLinus Torvalds {
4951da177e4SLinus Torvalds 	emu->ops.terminate(vp);
4961da177e4SLinus Torvalds 	vp->time = emu->use_time++;
4971da177e4SLinus Torvalds 	vp->chan = NULL;
4981da177e4SLinus Torvalds 	vp->port = NULL;
4991da177e4SLinus Torvalds 	vp->zone = NULL;
5001da177e4SLinus Torvalds 	vp->block = NULL;
5011da177e4SLinus Torvalds 	vp->state = SNDRV_EMUX_ST_OFF;
5021da177e4SLinus Torvalds 	if (free && emu->ops.free_voice)
5031da177e4SLinus Torvalds 		emu->ops.free_voice(vp);
5041da177e4SLinus Torvalds }
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds /*
5081da177e4SLinus Torvalds  * Modulate the voice
5091da177e4SLinus Torvalds  */
5101da177e4SLinus Torvalds static void
update_voice(struct snd_emux * emu,struct snd_emux_voice * vp,int update)51103da312aSTakashi Iwai update_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int update)
5121da177e4SLinus Torvalds {
5131da177e4SLinus Torvalds 	if (!STATE_IS_PLAYING(vp->state))
5141da177e4SLinus Torvalds 		return;
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 	if (vp->chan == NULL || vp->port == NULL)
5171da177e4SLinus Torvalds 		return;
5181da177e4SLinus Torvalds 	if (update & SNDRV_EMUX_UPDATE_VOLUME)
5191da177e4SLinus Torvalds 		calc_volume(vp);
5201da177e4SLinus Torvalds 	if (update & SNDRV_EMUX_UPDATE_PITCH)
5211da177e4SLinus Torvalds 		calc_pitch(vp);
5221da177e4SLinus Torvalds 	if (update & SNDRV_EMUX_UPDATE_PAN) {
5231da177e4SLinus Torvalds 		if (! calc_pan(vp) && (update == SNDRV_EMUX_UPDATE_PAN))
5241da177e4SLinus Torvalds 			return;
5251da177e4SLinus Torvalds 	}
5261da177e4SLinus Torvalds 	emu->ops.update(vp, update);
5271da177e4SLinus Torvalds }
5281da177e4SLinus Torvalds 
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds #if 0 // not used
5311da177e4SLinus Torvalds /* table for volume target calculation */
53255a6921bSTakashi Iwai static const unsigned short voltarget[16] = {
5331da177e4SLinus Torvalds 	0xEAC0, 0xE0C8, 0xD740, 0xCE20, 0xC560, 0xBD08, 0xB500, 0xAD58,
5341da177e4SLinus Torvalds 	0xA5F8, 0x9EF0, 0x9830, 0x91C0, 0x8B90, 0x85A8, 0x8000, 0x7A90
5351da177e4SLinus Torvalds };
5361da177e4SLinus Torvalds #endif
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds #define LO_BYTE(v)	((v) & 0xff)
5391da177e4SLinus Torvalds #define HI_BYTE(v)	(((v) >> 8) & 0xff)
5401da177e4SLinus Torvalds 
5411da177e4SLinus Torvalds /*
5421da177e4SLinus Torvalds  * Sets up the voice structure by calculating some values that
5431da177e4SLinus Torvalds  * will be needed later.
5441da177e4SLinus Torvalds  */
5451da177e4SLinus Torvalds static void
setup_voice(struct snd_emux_voice * vp)54603da312aSTakashi Iwai setup_voice(struct snd_emux_voice *vp)
5471da177e4SLinus Torvalds {
54803da312aSTakashi Iwai 	struct soundfont_voice_parm *parm;
5491da177e4SLinus Torvalds 	int pitch;
5501da177e4SLinus Torvalds 
5511da177e4SLinus Torvalds 	/* copy the original register values */
5521da177e4SLinus Torvalds 	vp->reg = vp->zone->v;
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds #ifdef SNDRV_EMUX_USE_RAW_EFFECT
5551da177e4SLinus Torvalds 	snd_emux_setup_effect(vp);
5561da177e4SLinus Torvalds #endif
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	/* reset status */
5591da177e4SLinus Torvalds 	vp->apan = -1;
5601da177e4SLinus Torvalds 	vp->avol = -1;
5611da177e4SLinus Torvalds 	vp->apitch = -1;
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 	calc_volume(vp);
5641da177e4SLinus Torvalds 	calc_pitch(vp);
5651da177e4SLinus Torvalds 	calc_pan(vp);
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds 	parm = &vp->reg.parm;
5681da177e4SLinus Torvalds 
5691da177e4SLinus Torvalds 	/* compute filter target and correct modulation parameters */
5701da177e4SLinus Torvalds 	if (LO_BYTE(parm->modatkhld) >= 0x80 && parm->moddelay >= 0x8000) {
5711da177e4SLinus Torvalds 		parm->moddelay = 0xbfff;
5721da177e4SLinus Torvalds 		pitch = (HI_BYTE(parm->pefe) << 4) + vp->apitch;
5731da177e4SLinus Torvalds 		if (pitch > 0xffff)
5741da177e4SLinus Torvalds 			pitch = 0xffff;
5751da177e4SLinus Torvalds 		/* calculate filter target */
5761da177e4SLinus Torvalds 		vp->ftarget = parm->cutoff + LO_BYTE(parm->pefe);
5771da177e4SLinus Torvalds 		LIMITVALUE(vp->ftarget, 0, 255);
5781da177e4SLinus Torvalds 		vp->ftarget <<= 8;
5791da177e4SLinus Torvalds 	} else {
5801da177e4SLinus Torvalds 		vp->ftarget = parm->cutoff;
5811da177e4SLinus Torvalds 		vp->ftarget <<= 8;
5821da177e4SLinus Torvalds 		pitch = vp->apitch;
5831da177e4SLinus Torvalds 	}
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds 	/* compute pitch target */
5861da177e4SLinus Torvalds 	if (pitch != 0xffff) {
5871da177e4SLinus Torvalds 		vp->ptarget = 1 << (pitch >> 12);
5881da177e4SLinus Torvalds 		if (pitch & 0x800) vp->ptarget += (vp->ptarget*0x102e)/0x2710;
5891da177e4SLinus Torvalds 		if (pitch & 0x400) vp->ptarget += (vp->ptarget*0x764)/0x2710;
5901da177e4SLinus Torvalds 		if (pitch & 0x200) vp->ptarget += (vp->ptarget*0x389)/0x2710;
5911da177e4SLinus Torvalds 		vp->ptarget += (vp->ptarget >> 1);
5921da177e4SLinus Torvalds 		if (vp->ptarget > 0xffff) vp->ptarget = 0xffff;
5931da177e4SLinus Torvalds 	} else
5941da177e4SLinus Torvalds 		vp->ptarget = 0xffff;
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds 	if (LO_BYTE(parm->modatkhld) >= 0x80) {
5971da177e4SLinus Torvalds 		parm->modatkhld &= ~0xff;
5981da177e4SLinus Torvalds 		parm->modatkhld |= 0x7f;
5991da177e4SLinus Torvalds 	}
6001da177e4SLinus Torvalds 
6011da177e4SLinus Torvalds 	/* compute volume target and correct volume parameters */
6021da177e4SLinus Torvalds 	vp->vtarget = 0;
6031da177e4SLinus Torvalds #if 0 /* FIXME: this leads to some clicks.. */
6041da177e4SLinus Torvalds 	if (LO_BYTE(parm->volatkhld) >= 0x80 && parm->voldelay >= 0x8000) {
6051da177e4SLinus Torvalds 		parm->voldelay = 0xbfff;
6061da177e4SLinus Torvalds 		vp->vtarget = voltarget[vp->avol % 0x10] >> (vp->avol >> 4);
6071da177e4SLinus Torvalds 	}
6081da177e4SLinus Torvalds #endif
6091da177e4SLinus Torvalds 
6101da177e4SLinus Torvalds 	if (LO_BYTE(parm->volatkhld) >= 0x80) {
6111da177e4SLinus Torvalds 		parm->volatkhld &= ~0xff;
6121da177e4SLinus Torvalds 		parm->volatkhld |= 0x7f;
6131da177e4SLinus Torvalds 	}
6141da177e4SLinus Torvalds }
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds /*
6171da177e4SLinus Torvalds  * calculate pitch parameter
6181da177e4SLinus Torvalds  */
61955a6921bSTakashi Iwai static const unsigned char pan_volumes[256] = {
6201da177e4SLinus Torvalds 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x14,0x17,0x1a,0x1d,0x20,0x22,0x25,0x28,0x2a,
6211da177e4SLinus Torvalds 0x2d,0x30,0x32,0x35,0x37,0x3a,0x3c,0x3f,0x41,0x44,0x46,0x49,0x4b,0x4d,0x50,0x52,
6221da177e4SLinus Torvalds 0x54,0x57,0x59,0x5b,0x5d,0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6f,0x71,0x73,0x75,
6231da177e4SLinus Torvalds 0x77,0x79,0x7b,0x7c,0x7e,0x80,0x82,0x84,0x86,0x88,0x89,0x8b,0x8d,0x8f,0x90,0x92,
6241da177e4SLinus Torvalds 0x94,0x96,0x97,0x99,0x9a,0x9c,0x9e,0x9f,0xa1,0xa2,0xa4,0xa5,0xa7,0xa8,0xaa,0xab,
6251da177e4SLinus Torvalds 0xad,0xae,0xaf,0xb1,0xb2,0xb3,0xb5,0xb6,0xb7,0xb9,0xba,0xbb,0xbc,0xbe,0xbf,0xc0,
6261da177e4SLinus Torvalds 0xc1,0xc2,0xc3,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,0xd0,0xd1,
6271da177e4SLinus Torvalds 0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdc,0xdd,0xde,0xdf,
6281da177e4SLinus Torvalds 0xdf,0xe0,0xe1,0xe2,0xe2,0xe3,0xe4,0xe4,0xe5,0xe6,0xe6,0xe7,0xe8,0xe8,0xe9,0xe9,
6291da177e4SLinus Torvalds 0xea,0xeb,0xeb,0xec,0xec,0xed,0xed,0xee,0xee,0xef,0xef,0xf0,0xf0,0xf1,0xf1,0xf1,
6301da177e4SLinus Torvalds 0xf2,0xf2,0xf3,0xf3,0xf3,0xf4,0xf4,0xf5,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,0xf7,0xf7,
6311da177e4SLinus Torvalds 0xf7,0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfa,0xfb,0xfb,0xfb,
6321da177e4SLinus Torvalds 0xfb,0xfb,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,0xfd,
6331da177e4SLinus Torvalds 0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,
6341da177e4SLinus Torvalds 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
6351da177e4SLinus Torvalds 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
6361da177e4SLinus Torvalds };
6371da177e4SLinus Torvalds 
6381da177e4SLinus Torvalds static int
calc_pan(struct snd_emux_voice * vp)63903da312aSTakashi Iwai calc_pan(struct snd_emux_voice *vp)
6401da177e4SLinus Torvalds {
64103da312aSTakashi Iwai 	struct snd_midi_channel *chan = vp->chan;
6421da177e4SLinus Torvalds 	int pan;
6431da177e4SLinus Torvalds 
6441da177e4SLinus Torvalds 	/* pan & loop start (pan 8bit, MSB, 0:right, 0xff:left) */
6451da177e4SLinus Torvalds 	if (vp->reg.fixpan > 0)	/* 0-127 */
6461da177e4SLinus Torvalds 		pan = 255 - (int)vp->reg.fixpan * 2;
6471da177e4SLinus Torvalds 	else {
6481da177e4SLinus Torvalds 		pan = chan->control[MIDI_CTL_MSB_PAN] - 64;
6491da177e4SLinus Torvalds 		if (vp->reg.pan >= 0) /* 0-127 */
6501da177e4SLinus Torvalds 			pan += vp->reg.pan - 64;
6511da177e4SLinus Torvalds 		pan = 127 - (int)pan * 2;
6521da177e4SLinus Torvalds 	}
6531da177e4SLinus Torvalds 	LIMITVALUE(pan, 0, 255);
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds 	if (vp->emu->linear_panning) {
6561da177e4SLinus Torvalds 		/* assuming linear volume */
6571da177e4SLinus Torvalds 		if (pan != vp->apan) {
6581da177e4SLinus Torvalds 			vp->apan = pan;
6591da177e4SLinus Torvalds 			if (pan == 0)
6601da177e4SLinus Torvalds 				vp->aaux = 0xff;
6611da177e4SLinus Torvalds 			else
6621da177e4SLinus Torvalds 				vp->aaux = (-pan) & 0xff;
6631da177e4SLinus Torvalds 			return 1;
6641da177e4SLinus Torvalds 		} else
6651da177e4SLinus Torvalds 			return 0;
6661da177e4SLinus Torvalds 	} else {
6671da177e4SLinus Torvalds 		/* using volume table */
6681da177e4SLinus Torvalds 		if (vp->apan != (int)pan_volumes[pan]) {
6691da177e4SLinus Torvalds 			vp->apan = pan_volumes[pan];
6701da177e4SLinus Torvalds 			vp->aaux = pan_volumes[255 - pan];
6711da177e4SLinus Torvalds 			return 1;
6721da177e4SLinus Torvalds 		}
6731da177e4SLinus Torvalds 		return 0;
6741da177e4SLinus Torvalds 	}
6751da177e4SLinus Torvalds }
6761da177e4SLinus Torvalds 
6771da177e4SLinus Torvalds 
6781da177e4SLinus Torvalds /*
6791da177e4SLinus Torvalds  * calculate volume attenuation
6801da177e4SLinus Torvalds  *
6811da177e4SLinus Torvalds  * Voice volume is controlled by volume attenuation parameter.
6821da177e4SLinus Torvalds  * So volume becomes maximum when avol is 0 (no attenuation), and
6831da177e4SLinus Torvalds  * minimum when 255 (-96dB or silence).
6841da177e4SLinus Torvalds  */
6851da177e4SLinus Torvalds 
6861da177e4SLinus Torvalds /* tables for volume->attenuation calculation */
68755a6921bSTakashi Iwai static const unsigned char voltab1[128] = {
6881da177e4SLinus Torvalds    0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
6891da177e4SLinus Torvalds    0x63, 0x2b, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22,
6901da177e4SLinus Torvalds    0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a,
6911da177e4SLinus Torvalds    0x19, 0x19, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x14,
6921da177e4SLinus Torvalds    0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10,
6931da177e4SLinus Torvalds    0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d,
6941da177e4SLinus Torvalds    0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b,
6951da177e4SLinus Torvalds    0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09,
6961da177e4SLinus Torvalds    0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06,
6971da177e4SLinus Torvalds    0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04,
6981da177e4SLinus Torvalds    0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
6991da177e4SLinus Torvalds    0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,
7001da177e4SLinus Torvalds    0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
7011da177e4SLinus Torvalds };
7021da177e4SLinus Torvalds 
70355a6921bSTakashi Iwai static const unsigned char voltab2[128] = {
7041da177e4SLinus Torvalds    0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x2a,
7051da177e4SLinus Torvalds    0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x24, 0x23, 0x22, 0x21,
7061da177e4SLinus Torvalds    0x21, 0x20, 0x1f, 0x1e, 0x1e, 0x1d, 0x1c, 0x1c, 0x1b, 0x1a,
7071da177e4SLinus Torvalds    0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, 0x16, 0x16, 0x15, 0x15,
7081da177e4SLinus Torvalds    0x14, 0x14, 0x13, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, 0x10,
7091da177e4SLinus Torvalds    0x10, 0x10, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d,
7101da177e4SLinus Torvalds    0x0d, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a,
7111da177e4SLinus Torvalds    0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08,
7121da177e4SLinus Torvalds    0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
7131da177e4SLinus Torvalds    0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
7141da177e4SLinus Torvalds    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,
7151da177e4SLinus Torvalds    0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
7161da177e4SLinus Torvalds    0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
7171da177e4SLinus Torvalds };
7181da177e4SLinus Torvalds 
71955a6921bSTakashi Iwai static const unsigned char expressiontab[128] = {
7201da177e4SLinus Torvalds    0x7f, 0x6c, 0x62, 0x5a, 0x54, 0x50, 0x4b, 0x48, 0x45, 0x42,
7211da177e4SLinus Torvalds    0x40, 0x3d, 0x3b, 0x39, 0x38, 0x36, 0x34, 0x33, 0x31, 0x30,
7221da177e4SLinus Torvalds    0x2f, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25,
7231da177e4SLinus Torvalds    0x24, 0x24, 0x23, 0x22, 0x21, 0x21, 0x20, 0x1f, 0x1e, 0x1e,
7241da177e4SLinus Torvalds    0x1d, 0x1d, 0x1c, 0x1b, 0x1b, 0x1a, 0x1a, 0x19, 0x18, 0x18,
7251da177e4SLinus Torvalds    0x17, 0x17, 0x16, 0x16, 0x15, 0x15, 0x15, 0x14, 0x14, 0x13,
7261da177e4SLinus Torvalds    0x13, 0x12, 0x12, 0x11, 0x11, 0x11, 0x10, 0x10, 0x0f, 0x0f,
7271da177e4SLinus Torvalds    0x0f, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c,
7281da177e4SLinus Torvalds    0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09,
7291da177e4SLinus Torvalds    0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,
7301da177e4SLinus Torvalds    0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03,
7311da177e4SLinus Torvalds    0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
7321da177e4SLinus Torvalds    0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
7331da177e4SLinus Torvalds };
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds /*
7361da177e4SLinus Torvalds  * Magic to calculate the volume (actually attenuation) from all the
7371da177e4SLinus Torvalds  * voice and channels parameters.
7381da177e4SLinus Torvalds  */
7391da177e4SLinus Torvalds static int
calc_volume(struct snd_emux_voice * vp)74003da312aSTakashi Iwai calc_volume(struct snd_emux_voice *vp)
7411da177e4SLinus Torvalds {
7421da177e4SLinus Torvalds 	int vol;
7431da177e4SLinus Torvalds 	int main_vol, expression_vol, master_vol;
74403da312aSTakashi Iwai 	struct snd_midi_channel *chan = vp->chan;
74503da312aSTakashi Iwai 	struct snd_emux_port *port = vp->port;
7461da177e4SLinus Torvalds 
7471da177e4SLinus Torvalds 	expression_vol = chan->control[MIDI_CTL_MSB_EXPRESSION];
7481da177e4SLinus Torvalds 	LIMITMAX(vp->velocity, 127);
7491da177e4SLinus Torvalds 	LIMITVALUE(expression_vol, 0, 127);
7501da177e4SLinus Torvalds 	if (port->port_mode == SNDRV_EMUX_PORT_MODE_OSS_SYNTH) {
7511da177e4SLinus Torvalds 		/* 0 - 127 */
7521da177e4SLinus Torvalds 		main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME];
7531da177e4SLinus Torvalds 		vol = (vp->velocity * main_vol * expression_vol) / (127*127);
7541da177e4SLinus Torvalds 		vol = vol * vp->reg.amplitude / 127;
7551da177e4SLinus Torvalds 
7561da177e4SLinus Torvalds 		LIMITVALUE(vol, 0, 127);
7571da177e4SLinus Torvalds 
7581da177e4SLinus Torvalds 		/* calc to attenuation */
7591da177e4SLinus Torvalds 		vol = snd_sf_vol_table[vol];
7601da177e4SLinus Torvalds 
7611da177e4SLinus Torvalds 	} else {
7621da177e4SLinus Torvalds 		main_vol = chan->control[MIDI_CTL_MSB_MAIN_VOLUME] * vp->reg.amplitude / 127;
7631da177e4SLinus Torvalds 		LIMITVALUE(main_vol, 0, 127);
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds 		vol = voltab1[main_vol] + voltab2[vp->velocity];
7661da177e4SLinus Torvalds 		vol = (vol * 8) / 3;
7671da177e4SLinus Torvalds 		vol += vp->reg.attenuation;
7681da177e4SLinus Torvalds 		vol += ((0x100 - vol) * expressiontab[expression_vol])/128;
7691da177e4SLinus Torvalds 	}
7701da177e4SLinus Torvalds 
7711da177e4SLinus Torvalds 	master_vol = port->chset.gs_master_volume;
7721da177e4SLinus Torvalds 	LIMITVALUE(master_vol, 0, 127);
7731da177e4SLinus Torvalds 	vol += snd_sf_vol_table[master_vol];
7741da177e4SLinus Torvalds 	vol += port->volume_atten;
7751da177e4SLinus Torvalds 
7761da177e4SLinus Torvalds #ifdef SNDRV_EMUX_USE_RAW_EFFECT
7771da177e4SLinus Torvalds 	if (chan->private) {
77803da312aSTakashi Iwai 		struct snd_emux_effect_table *fx = chan->private;
7791da177e4SLinus Torvalds 		vol += fx->val[EMUX_FX_ATTEN];
7801da177e4SLinus Torvalds 	}
7811da177e4SLinus Torvalds #endif
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds 	LIMITVALUE(vol, 0, 255);
7841da177e4SLinus Torvalds 	if (vp->avol == vol)
7851da177e4SLinus Torvalds 		return 0; /* value unchanged */
7861da177e4SLinus Torvalds 
7871da177e4SLinus Torvalds 	vp->avol = vol;
7881da177e4SLinus Torvalds 	if (!SF_IS_DRUM_BANK(get_bank(port, chan))
7891da177e4SLinus Torvalds 	    && LO_BYTE(vp->reg.parm.volatkhld) < 0x7d) {
7901da177e4SLinus Torvalds 		int atten;
7911da177e4SLinus Torvalds 		if (vp->velocity < 70)
7921da177e4SLinus Torvalds 			atten = 70;
7931da177e4SLinus Torvalds 		else
7941da177e4SLinus Torvalds 			atten = vp->velocity;
7951da177e4SLinus Torvalds 		vp->acutoff = (atten * vp->reg.parm.cutoff + 0xa0) >> 7;
7961da177e4SLinus Torvalds 	} else {
7971da177e4SLinus Torvalds 		vp->acutoff = vp->reg.parm.cutoff;
7981da177e4SLinus Torvalds 	}
7991da177e4SLinus Torvalds 
8001da177e4SLinus Torvalds 	return 1; /* value changed */
8011da177e4SLinus Torvalds }
8021da177e4SLinus Torvalds 
8031da177e4SLinus Torvalds /*
8041da177e4SLinus Torvalds  * calculate pitch offset
8051da177e4SLinus Torvalds  *
8061da177e4SLinus Torvalds  * 0xE000 is no pitch offset at 44100Hz sample.
8071da177e4SLinus Torvalds  * Every 4096 is one octave.
8081da177e4SLinus Torvalds  */
8091da177e4SLinus Torvalds 
8101da177e4SLinus Torvalds static int
calc_pitch(struct snd_emux_voice * vp)81103da312aSTakashi Iwai calc_pitch(struct snd_emux_voice *vp)
8121da177e4SLinus Torvalds {
81303da312aSTakashi Iwai 	struct snd_midi_channel *chan = vp->chan;
8141da177e4SLinus Torvalds 	int offset;
8151da177e4SLinus Torvalds 
8161da177e4SLinus Torvalds 	/* calculate offset */
8171da177e4SLinus Torvalds 	if (vp->reg.fixkey >= 0) {
8181da177e4SLinus Torvalds 		offset = (vp->reg.fixkey - vp->reg.root) * 4096 / 12;
8191da177e4SLinus Torvalds 	} else {
8201da177e4SLinus Torvalds 		offset = (vp->note - vp->reg.root) * 4096 / 12;
8211da177e4SLinus Torvalds 	}
8221da177e4SLinus Torvalds 	offset = (offset * vp->reg.scaleTuning) / 100;
8231da177e4SLinus Torvalds 	offset += vp->reg.tune * 4096 / 1200;
8241da177e4SLinus Torvalds 	if (chan->midi_pitchbend != 0) {
8251da177e4SLinus Torvalds 		/* (128 * 8192: 1 semitone) ==> (4096: 12 semitones) */
8261da177e4SLinus Torvalds 		offset += chan->midi_pitchbend * chan->gm_rpn_pitch_bend_range / 3072;
8271da177e4SLinus Torvalds 	}
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds 	/* tuning via RPN:
8301da177e4SLinus Torvalds 	 *   coarse = -8192 to 8192 (100 cent per 128)
8311da177e4SLinus Torvalds 	 *   fine = -8192 to 8192 (max=100cent)
8321da177e4SLinus Torvalds 	 */
8331da177e4SLinus Torvalds 	/* 4096 = 1200 cents in emu8000 parameter */
8341da177e4SLinus Torvalds 	offset += chan->gm_rpn_coarse_tuning * 4096 / (12 * 128);
8351da177e4SLinus Torvalds 	offset += chan->gm_rpn_fine_tuning / 24;
8361da177e4SLinus Torvalds 
8371da177e4SLinus Torvalds #ifdef SNDRV_EMUX_USE_RAW_EFFECT
8381da177e4SLinus Torvalds 	/* add initial pitch correction */
8391da177e4SLinus Torvalds 	if (chan->private) {
84003da312aSTakashi Iwai 		struct snd_emux_effect_table *fx = chan->private;
8411da177e4SLinus Torvalds 		if (fx->flag[EMUX_FX_INIT_PITCH])
8421da177e4SLinus Torvalds 			offset += fx->val[EMUX_FX_INIT_PITCH];
8431da177e4SLinus Torvalds 	}
8441da177e4SLinus Torvalds #endif
8451da177e4SLinus Torvalds 
8461da177e4SLinus Torvalds 	/* 0xe000: root pitch */
8471da177e4SLinus Torvalds 	offset += 0xe000 + vp->reg.rate_offset;
848*e68235c8SOswald Buddenhagen 	if (vp->emu->ops.get_pitch_shift)
849*e68235c8SOswald Buddenhagen 		offset += vp->emu->ops.get_pitch_shift(vp->emu);
8501da177e4SLinus Torvalds 	LIMITVALUE(offset, 0, 0xffff);
8511da177e4SLinus Torvalds 	if (offset == vp->apitch)
8521da177e4SLinus Torvalds 		return 0; /* unchanged */
8531da177e4SLinus Torvalds 	vp->apitch = offset;
8541da177e4SLinus Torvalds 	return 1; /* value changed */
8551da177e4SLinus Torvalds }
8561da177e4SLinus Torvalds 
8571da177e4SLinus Torvalds /*
8581da177e4SLinus Torvalds  * Get the bank number assigned to the channel
8591da177e4SLinus Torvalds  */
8601da177e4SLinus Torvalds static int
get_bank(struct snd_emux_port * port,struct snd_midi_channel * chan)86103da312aSTakashi Iwai get_bank(struct snd_emux_port *port, struct snd_midi_channel *chan)
8621da177e4SLinus Torvalds {
8631da177e4SLinus Torvalds 	int val;
8641da177e4SLinus Torvalds 
8651da177e4SLinus Torvalds 	switch (port->chset.midi_mode) {
8661da177e4SLinus Torvalds 	case SNDRV_MIDI_MODE_XG:
8671da177e4SLinus Torvalds 		val = chan->control[MIDI_CTL_MSB_BANK];
8681da177e4SLinus Torvalds 		if (val == 127)
8691da177e4SLinus Torvalds 			return 128; /* return drum bank */
8701da177e4SLinus Torvalds 		return chan->control[MIDI_CTL_LSB_BANK];
8711da177e4SLinus Torvalds 
8721da177e4SLinus Torvalds 	case SNDRV_MIDI_MODE_GS:
8731da177e4SLinus Torvalds 		if (chan->drum_channel)
8741da177e4SLinus Torvalds 			return 128;
8751da177e4SLinus Torvalds 		/* ignore LSB (bank map) */
8761da177e4SLinus Torvalds 		return chan->control[MIDI_CTL_MSB_BANK];
8771da177e4SLinus Torvalds 
8781da177e4SLinus Torvalds 	default:
8791da177e4SLinus Torvalds 		if (chan->drum_channel)
8801da177e4SLinus Torvalds 			return 128;
8811da177e4SLinus Torvalds 		return chan->control[MIDI_CTL_MSB_BANK];
8821da177e4SLinus Torvalds 	}
8831da177e4SLinus Torvalds }
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds 
8861da177e4SLinus Torvalds /* Look for the zones matching with the given note and velocity.
8871da177e4SLinus Torvalds  * The resultant zones are stored on table.
8881da177e4SLinus Torvalds  */
8891da177e4SLinus Torvalds static int
get_zone(struct snd_emux * emu,struct snd_emux_port * port,int * notep,int vel,struct snd_midi_channel * chan,struct snd_sf_zone ** table)89003da312aSTakashi Iwai get_zone(struct snd_emux *emu, struct snd_emux_port *port,
89103da312aSTakashi Iwai 	 int *notep, int vel, struct snd_midi_channel *chan,
89203da312aSTakashi Iwai 	 struct snd_sf_zone **table)
8931da177e4SLinus Torvalds {
8941da177e4SLinus Torvalds 	int preset, bank, def_preset, def_bank;
8951da177e4SLinus Torvalds 
8961da177e4SLinus Torvalds 	bank = get_bank(port, chan);
8971da177e4SLinus Torvalds 	preset = chan->midi_program;
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds 	if (SF_IS_DRUM_BANK(bank)) {
9001da177e4SLinus Torvalds 		def_preset = port->ctrls[EMUX_MD_DEF_DRUM];
9011da177e4SLinus Torvalds 		def_bank = bank;
9021da177e4SLinus Torvalds 	} else {
9031da177e4SLinus Torvalds 		def_preset = preset;
9041da177e4SLinus Torvalds 		def_bank = port->ctrls[EMUX_MD_DEF_BANK];
9051da177e4SLinus Torvalds 	}
9061da177e4SLinus Torvalds 
9071da177e4SLinus Torvalds 	return snd_soundfont_search_zone(emu->sflist, notep, vel, preset, bank,
9081da177e4SLinus Torvalds 					 def_preset, def_bank,
9091da177e4SLinus Torvalds 					 table, SNDRV_EMUX_MAX_MULTI_VOICES);
9101da177e4SLinus Torvalds }
9111da177e4SLinus Torvalds 
9121da177e4SLinus Torvalds /*
9131da177e4SLinus Torvalds  */
9141da177e4SLinus Torvalds void
snd_emux_init_voices(struct snd_emux * emu)91503da312aSTakashi Iwai snd_emux_init_voices(struct snd_emux *emu)
9161da177e4SLinus Torvalds {
91703da312aSTakashi Iwai 	struct snd_emux_voice *vp;
9181da177e4SLinus Torvalds 	int i;
9191da177e4SLinus Torvalds 	unsigned long flags;
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
9221da177e4SLinus Torvalds 	for (i = 0; i < emu->max_voices; i++) {
9231da177e4SLinus Torvalds 		vp = &emu->voices[i];
9241da177e4SLinus Torvalds 		vp->ch = -1; /* not used */
9251da177e4SLinus Torvalds 		vp->state = SNDRV_EMUX_ST_OFF;
9261da177e4SLinus Torvalds 		vp->chan = NULL;
9271da177e4SLinus Torvalds 		vp->port = NULL;
9281da177e4SLinus Torvalds 		vp->time = 0;
9291da177e4SLinus Torvalds 		vp->emu = emu;
9301da177e4SLinus Torvalds 		vp->hw = emu->hw;
9311da177e4SLinus Torvalds 	}
9321da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
9331da177e4SLinus Torvalds }
9341da177e4SLinus Torvalds 
9351da177e4SLinus Torvalds /*
9361da177e4SLinus Torvalds  */
snd_emux_lock_voice(struct snd_emux * emu,int voice)93703da312aSTakashi Iwai void snd_emux_lock_voice(struct snd_emux *emu, int voice)
9381da177e4SLinus Torvalds {
9391da177e4SLinus Torvalds 	unsigned long flags;
9401da177e4SLinus Torvalds 
9411da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
9421da177e4SLinus Torvalds 	if (emu->voices[voice].state == SNDRV_EMUX_ST_OFF)
9431da177e4SLinus Torvalds 		emu->voices[voice].state = SNDRV_EMUX_ST_LOCKED;
9441da177e4SLinus Torvalds 	else
94542b0158bSTakashi Iwai 		snd_printk(KERN_WARNING
94642b0158bSTakashi Iwai 			   "invalid voice for lock %d (state = %x)\n",
9471da177e4SLinus Torvalds 			   voice, emu->voices[voice].state);
9481da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
9491da177e4SLinus Torvalds }
9501da177e4SLinus Torvalds 
95195ff1756STakashi Iwai EXPORT_SYMBOL(snd_emux_lock_voice);
95295ff1756STakashi Iwai 
9531da177e4SLinus Torvalds /*
9541da177e4SLinus Torvalds  */
snd_emux_unlock_voice(struct snd_emux * emu,int voice)95503da312aSTakashi Iwai void snd_emux_unlock_voice(struct snd_emux *emu, int voice)
9561da177e4SLinus Torvalds {
9571da177e4SLinus Torvalds 	unsigned long flags;
9581da177e4SLinus Torvalds 
9591da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
9601da177e4SLinus Torvalds 	if (emu->voices[voice].state == SNDRV_EMUX_ST_LOCKED)
9611da177e4SLinus Torvalds 		emu->voices[voice].state = SNDRV_EMUX_ST_OFF;
9621da177e4SLinus Torvalds 	else
96342b0158bSTakashi Iwai 		snd_printk(KERN_WARNING
96442b0158bSTakashi Iwai 			   "invalid voice for unlock %d (state = %x)\n",
9651da177e4SLinus Torvalds 			   voice, emu->voices[voice].state);
9661da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
9671da177e4SLinus Torvalds }
96895ff1756STakashi Iwai 
96995ff1756STakashi Iwai EXPORT_SYMBOL(snd_emux_unlock_voice);
970