diff mbox series

[14/14] ALSA: emu10k1: enable bit-exact playback, part 2: voice attenuation

Message ID 20230510173917.3073107-15-oswald.buddenhagen@gmx.de
State Accepted
Commit bcdbd3b7888e1db89b7b2f7c78237c9ed5c2ebb1
Headers show
Series ALSA: emu10k1: various improvements to the DSP-based mixer code | expand

Commit Message

Oswald Buddenhagen May 10, 2023, 5:39 p.m. UTC
The voice volume is a raw fractional multiplier that can't actually
represent 1.0. To still enable real pass-through, we now set the volume
to 0.5 (which results in no loss of precision, as the FX bus provides
fractional values) and scale up the samples in DSP code.

To maintain backwards compatibility with existing configuration files,
we rescale the values in the mixer controls. The range is extended
upwards from 0xffff to 0x1fffd, which actually introduces the
possibility of specifying an amplification.

There is still a minor incompatibility with user space, namely if
someone loaded custom DSP code. They'll just get half the volume, so
this doesn't seem like a big deal.

Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
---
 Documentation/sound/cards/audigy-mixer.rst  |  2 +-
 Documentation/sound/cards/sb-live-mixer.rst |  2 +-
 include/sound/emu10k1.h                     |  3 +++
 sound/pci/emu10k1/emufx.c                   | 30 ++++++++++++---------
 sound/pci/emu10k1/emumixer.c                | 15 ++++++-----
 sound/pci/emu10k1/emupcm.c                  |  4 +--
 6 files changed, 34 insertions(+), 22 deletions(-)
diff mbox series

Patch

diff --git a/Documentation/sound/cards/audigy-mixer.rst b/Documentation/sound/cards/audigy-mixer.rst
index aa176451d5b5..e02dd890d089 100644
--- a/Documentation/sound/cards/audigy-mixer.rst
+++ b/Documentation/sound/cards/audigy-mixer.rst
@@ -227,7 +227,7 @@  PCM stream related controls
 
 name='EMU10K1 PCM Volume',index 0-31
 ------------------------------------
-Channel volume attenuation in range 0-0xffff. The maximum value (no
+Channel volume attenuation in range 0-0x1fffd. The middle value (no
 attenuation) is default. The channel mapping for three values is
 as follows:
 
diff --git a/Documentation/sound/cards/sb-live-mixer.rst b/Documentation/sound/cards/sb-live-mixer.rst
index 819886634400..4dd9bfe01bd8 100644
--- a/Documentation/sound/cards/sb-live-mixer.rst
+++ b/Documentation/sound/cards/sb-live-mixer.rst
@@ -258,7 +258,7 @@  PCM stream related controls
 
 ``name='EMU10K1 PCM Volume',index 0-31``
 ----------------------------------------
-Channel volume attenuation in range 0-0xffff. The maximum value (no
+Channel volume attenuation in range 0-0x1fffd. The middle value (no
 attenuation) is default. The channel mapping for three values is
 as follows:
 
diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h
index 12135121128b..00b07e697261 100644
--- a/include/sound/emu10k1.h
+++ b/include/sound/emu10k1.h
@@ -396,6 +396,7 @@ 
 #define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff	/* Linear level of channel output sent to FX send bus B	*/
 #define PTRX_FXSENDAMOUNT_B	0x08000001
 
+// Note: the volumes are raw multpliers, so real 100% is impossible.
 #define CVCF			0x02		/* Current volume and filter cutoff register		*/
 #define CVCF_CURRENTVOL_MASK	0xffff0000	/* Current linear volume of specified channel		*/
 #define CVCF_CURRENTVOL		0x10100002
@@ -1480,6 +1481,8 @@  struct snd_emu10k1_pcm_mixer {
 	/* mono, left, right x 8 sends (4 on emu10k1) */
 	unsigned char send_routing[3][8];
 	unsigned char send_volume[3][8];
+	// 0x8000 is neutral. The mixer code rescales it to 0xffff to maintain
+	// backwards compatibility with user space.
 	unsigned short attn[3];
 	struct snd_emu10k1_pcm *epcm;
 };
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 32d5ed7528e8..d31731a8e1b4 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -1361,7 +1361,13 @@  static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
 	A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
 	snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Capture Volume", gpr, 0);
 	gpr += 2;
-      
+
+	// We need to double the volume, as we configure the voices for half volume,
+	// which is necessary for bit-identical reproduction.
+	{ static_assert(stereo_mix == playback + SND_EMU10K1_PLAYBACK_CHANNELS); }
+	for (z = 0; z < SND_EMU10K1_PLAYBACK_CHANNELS + 2; z++)
+		A_OP(icode, &ptr, iACC3, A_GPR(playback + z), A_GPR(playback + z), A_GPR(playback + z), A_C_00000000);
+
 	/*
 	 * inputs
 	 */
@@ -1822,18 +1828,18 @@  static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
 	/*
 	 *  Process FX Buses
 	 */
-	OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000004);
-	OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000004);
-	OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000004);
-	OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000004);
-	OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000004);
-	OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000004);
-	OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000004);
-	OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000008);
+	OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000008);
+	OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000008);
+	OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000008);
+	OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000008);
+	OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000008);
+	OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000008);
+	OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000008);
 	OP(icode, &ptr, iMACINT0, GPR(8), C_00000000, C_00000000, C_00000000);	/* S/PDIF left */
 	OP(icode, &ptr, iMACINT0, GPR(9), C_00000000, C_00000000, C_00000000);	/* S/PDIF right */
-	OP(icode, &ptr, iMACINT0, GPR(10), C_00000000, FXBUS(FXBUS_PCM_LEFT_FRONT), C_00000004);
-	OP(icode, &ptr, iMACINT0, GPR(11), C_00000000, FXBUS(FXBUS_PCM_RIGHT_FRONT), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(10), C_00000000, FXBUS(FXBUS_PCM_LEFT_FRONT), C_00000008);
+	OP(icode, &ptr, iMACINT0, GPR(11), C_00000000, FXBUS(FXBUS_PCM_RIGHT_FRONT), C_00000008);
 
 	/* Raw S/PDIF PCM */
 	ipcm->substream = 0;
@@ -1927,7 +1933,7 @@  static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
 	
 	/* Wave Center/LFE Playback Volume */
 	OP(icode, &ptr, iACC3, GPR(tmp + 0), FXBUS(FXBUS_PCM_LEFT), FXBUS(FXBUS_PCM_RIGHT), C_00000000);
-	OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000002);
+	OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000004);
 	VOLUME(icode, &ptr, playback + 4, tmp + 0, gpr);
 	snd_emu10k1_init_mono_control(controls + i++, "Wave Center Playback Volume", gpr++, 0);
 	VOLUME(icode, &ptr, playback + 5, tmp + 0, gpr);
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index 610700be1e37..8fb2b984e13f 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1352,7 +1352,7 @@  static int snd_emu10k1_attn_info(struct snd_kcontrol *kcontrol, struct snd_ctl_e
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 3;
 	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 0xffff;
+	uinfo->value.integer.max = 0x1fffd;
 	return 0;
 }
 
@@ -1365,7 +1365,7 @@  static int snd_emu10k1_attn_get(struct snd_kcontrol *kcontrol,
 	int idx;
 
 	for (idx = 0; idx < 3; idx++)
-		ucontrol->value.integer.value[idx] = mix->attn[idx];
+		ucontrol->value.integer.value[idx] = mix->attn[idx] * 0xffffU / 0x8000U;
 	return 0;
 }
 
@@ -1380,7 +1380,8 @@  static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
 
 	spin_lock_irqsave(&emu->reg_lock, flags);
 	for (idx = 0; idx < 3; idx++) {
-		val = ucontrol->value.integer.value[idx] & 0xffff;
+		unsigned uval = ucontrol->value.integer.value[idx] & 0x1ffff;
+		val = uval * 0x8000U / 0xffffU;
 		if (mix->attn[idx] != val) {
 			mix->attn[idx] = val;
 			change = 1;
@@ -1547,32 +1548,34 @@  static int snd_emu10k1_efx_attn_info(struct snd_kcontrol *kcontrol, struct snd_c
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
 	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 0xffff;
+	uinfo->value.integer.max = 0x1fffd;
 	return 0;
 }
 
 static int snd_emu10k1_efx_attn_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
 	struct snd_emu10k1_pcm_mixer *mix =
 		&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
 
-	ucontrol->value.integer.value[0] = mix->attn[0];
+	ucontrol->value.integer.value[0] = mix->attn[0] * 0xffffU / 0x8000U;
 	return 0;
 }
 
 static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
 	unsigned long flags;
 	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
 	int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
 	int change = 0, val;
+	unsigned uval;
 
 	spin_lock_irqsave(&emu->reg_lock, flags);
-	val = ucontrol->value.integer.value[0] & 0xffff;
+	uval = ucontrol->value.integer.value[0] & 0x1ffff;
+	val = uval * 0x8000U / 0xffffU;
 	if (mix->attn[0] != val) {
 		mix->attn[0] = val;
 		change = 1;
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 5ed404e8ed39..6e6d3103ed90 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -1049,7 +1049,7 @@  static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream)
 		mix->send_routing[0][0] = i;
 		memset(&mix->send_volume, 0, sizeof(mix->send_volume));
 		mix->send_volume[0][0] = 255;
-		mix->attn[0] = 0xffff;
+		mix->attn[0] = 0x8000;
 		mix->epcm = epcm;
 		snd_emu10k1_pcm_efx_mixer_notify(emu, i, 1);
 	}
@@ -1098,7 +1098,7 @@  static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream)
 	memset(&mix->send_volume, 0, sizeof(mix->send_volume));
 	mix->send_volume[0][0] = mix->send_volume[0][1] =
 	mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
-	mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+	mix->attn[0] = mix->attn[1] = mix->attn[2] = 0x8000;
 	mix->epcm = epcm;
 	snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1);
 	return 0;