ALSA: emu10k1: enable bit-exact playback, part 2: voice attenuation
authorOswald Buddenhagen <oswald.buddenhagen@gmx.de>
Sun, 14 May 2023 17:03:23 +0000 (19:03 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 16 May 2023 09:11:04 +0000 (11:11 +0200)
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>
Link: https://lore.kernel.org/r/20230514170323.3408834-8-oswald.buddenhagen@gmx.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Documentation/sound/cards/audigy-mixer.rst
Documentation/sound/cards/sb-live-mixer.rst
include/sound/emu10k1.h
sound/pci/emu10k1/emufx.c
sound/pci/emu10k1/emumixer.c
sound/pci/emu10k1/emupcm.c

index aa176451d5b58cfc8f1b44dadb6bdeabf0ecd4a9..e02dd890d089be9da198183f369cc3ac676f86b0 100644 (file)
@@ -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:
 
index 819886634400f22f1cf1336fa1ef578cc5ed2e91..4dd9bfe01bd87b841f938029ff82a9ea321be355 100644 (file)
@@ -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:
 
index 8e27f70742304341a1f8235a0c0253c88e5cc800..7bcb1a2d779a2d029a8b9cb26550b5be77d6be25 100644 (file)
@@ -415,6 +415,7 @@ SUB_REG(PTRX, PITCHTARGET,  0xffff0000)     /* Pitch target of specified channel                    */
 SUB_REG(PTRX, FXSENDAMOUNT_A,  0x0000ff00)     /* Linear level of channel output sent to FX send bus A */
 SUB_REG(PTRX, FXSENDAMOUNT_B,  0x000000ff)     /* Linear level of channel output sent to FX send bus B */
 
+// Note: the volumes are raw multpliers, so real 100% is impossible.
 #define CVCF                   0x02            /* Current volume and filter cutoff register            */
 SUB_REG(CVCF, CURRENTVOL,      0xffff0000)     /* Current linear volume of specified channel           */
 SUB_REG(CVCF, CURRENTFILTER,   0x0000ffff)     /* Current filter cutoff frequency of specified channel */
@@ -1477,6 +1478,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;
 };
index 4c9d67e72ae57eb303199682f00359f5e666696f..f64b2b4eb348e7bca4e4dee8920d5bca1e644689 100644 (file)
@@ -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
         */
@@ -1826,18 +1832,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;
@@ -1931,7 +1937,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);
index 48f0d3f8b8e704f2c86d8edbda0139451ec90953..9fa4bc8451160a676c4eb1fd4bc283c3bd949d53 100644 (file)
@@ -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,7 +1548,7 @@ 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;
 }
 
@@ -1558,7 +1559,7 @@ static int snd_emu10k1_efx_attn_get(struct snd_kcontrol *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;
 }
 
@@ -1570,9 +1571,11 @@ static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *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;
index 5ed404e8ed39c4182024b8bd9459437bc6cc7ed0..6e6d3103ed906b4d916cd1c33442d9856b69f108 100644 (file)
@@ -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;