ALSA: emu10k1: enable bit-exact playback, part 1: DSP attenuation
authorOswald Buddenhagen <oswald.buddenhagen@gmx.de>
Sun, 14 May 2023 17:03:22 +0000 (19:03 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 15 May 2023 20:06:21 +0000 (22:06 +0200)
Fractional multiplication with the maximal value 2^31-1 causes some tiny
distortion. Instead, we want to multiply with the full 2^31. The catch
is of course that this cannot be represented in the DSP's signed 32 bit
registers.

One way to deal with this is to encode 1.0 as a negative number and
special-case it. As a matter of fact, the SbLive! code path already
contained such code, though the controls never actually exercised it.

A more efficient approach is to use negative values, which actually
extend to -2^31. Accordingly, for all the volume adjustments we now use
the MAC1 instruction which negates the X operand.

The range of the controls in highres mode is extended downwards, so -1
is the new zero/mute. At maximal excursion, real zero is not mute any
more, but I don't think anyone will notice this behavior change. ;-)

That also required making the min/max/values in the control structs
signed. This technically changes the user space interface, but it seems
implausible that someone would notice - the numbers were actually
treated as if they were signed anyway (and in the actual mixer iface
they _are_). And without this change, the min value didn't even make
sense in the first place (and no-one noticed, because it was always 0).

Tested-by: Jonathan Dowland <jon@dow.land>
Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Link: https://lore.kernel.org/r/20230514170323.3408834-7-oswald.buddenhagen@gmx.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/sound/emu10k1.h
include/uapi/sound/emu10k1.h
sound/pci/emu10k1/emufx.c

index e9b1729..8e27f70 100644 (file)
@@ -1508,9 +1508,9 @@ struct snd_emu10k1_fx8010_ctl {
        unsigned int vcount;
        unsigned int count;             /* count of GPR (1..16) */
        unsigned short gpr[32];         /* GPR number(s) */
-       unsigned int value[32];
-       unsigned int min;               /* minimum range */
-       unsigned int max;               /* maximum range */
+       int value[32];
+       int min;                        /* minimum range */
+       int max;                        /* maximum range */
        unsigned int translation;       /* translation type (EMU10K1_GPR_TRANSLATION*) */
        struct snd_kcontrol *kcontrol;
 };
index c8e131d..4c32a11 100644 (file)
@@ -308,6 +308,8 @@ struct snd_emu10k1_fx8010_info {
 #define EMU10K1_GPR_TRANSLATION_BASS           2
 #define EMU10K1_GPR_TRANSLATION_TREBLE         3
 #define EMU10K1_GPR_TRANSLATION_ONOFF          4
+#define EMU10K1_GPR_TRANSLATION_NEGATE         5
+#define EMU10K1_GPR_TRANSLATION_NEG_TABLE100   6
 
 enum emu10k1_ctl_elem_iface {
        EMU10K1_CTL_ELEM_IFACE_MIXER = 2,       /* virtual mixer device */
@@ -328,9 +330,9 @@ struct snd_emu10k1_fx8010_control_gpr {
        unsigned int vcount;            /* visible count */
        unsigned int count;             /* count of GPR (1..16) */
        unsigned short gpr[32];         /* GPR number(s) */
-       unsigned int value[32];         /* initial values */
-       unsigned int min;               /* minimum range */
-       unsigned int max;               /* maximum range */
+       int value[32];                  /* initial values */
+       int min;                        /* minimum range */
+       int max;                        /* maximum range */
        unsigned int translation;       /* translation type (EMU10K1_GPR_TRANSLATION*) */
        const unsigned int *tlv;
 };
index 9c9ffba..4c9d67e 100644 (file)
@@ -332,7 +332,7 @@ static int snd_emu10k1_gpr_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl
        struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
        struct snd_emu10k1_fx8010_ctl *ctl =
                (struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
-       unsigned int nval, val;
+       int nval, val;
        unsigned int i, j;
        int change = 0;
        
@@ -349,9 +349,16 @@ static int snd_emu10k1_gpr_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl
                case EMU10K1_GPR_TRANSLATION_NONE:
                        snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, val);
                        break;
+               case EMU10K1_GPR_TRANSLATION_NEGATE:
+                       snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, ~val);
+                       break;
                case EMU10K1_GPR_TRANSLATION_TABLE100:
                        snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, db_table[val]);
                        break;
+               case EMU10K1_GPR_TRANSLATION_NEG_TABLE100:
+                       snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0,
+                                             val == 100 ? 0x80000000 : -(int)db_table[val]);
+                       break;
                case EMU10K1_GPR_TRANSLATION_BASS:
                        if ((ctl->count % 5) != 0 || (ctl->count / 5) != ctl->vcount) {
                                change = -EIO;
@@ -771,8 +778,10 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
                }
                switch (gctl->translation) {
                case EMU10K1_GPR_TRANSLATION_NONE:
+               case EMU10K1_GPR_TRANSLATION_NEGATE:
                        break;
                case EMU10K1_GPR_TRANSLATION_TABLE100:
+               case EMU10K1_GPR_TRANSLATION_NEG_TABLE100:
                        if (gctl->min != 0 || gctl->max != 100) {
                                err = -EINVAL;
                                goto __error;
@@ -1137,44 +1146,44 @@ static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
 
 static void
 snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
-                             const char *name, int gpr, int defval)
+                              const char *name, int gpr, int defval)
 {
        ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
        strcpy(ctl->id.name, name);
        ctl->vcount = ctl->count = 1;
        if (high_res_gpr_volume) {
-               ctl->min = 0;
+               ctl->min = -1;
                ctl->max = 0x7fffffff;
                ctl->tlv = snd_emu10k1_db_linear;
-               ctl->translation = EMU10K1_GPR_TRANSLATION_NONE;
-               defval = defval * 0x7fffffffLL / 100;
+               ctl->translation = EMU10K1_GPR_TRANSLATION_NEGATE;
+               defval = defval * 0x80000000LL / 100 - 1;
        } else {
                ctl->min = 0;
                ctl->max = 100;
                ctl->tlv = snd_emu10k1_db_scale1;
-               ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+               ctl->translation = EMU10K1_GPR_TRANSLATION_NEG_TABLE100;
        }
        ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
 }
 
 static void
 snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
-                               const char *name, int gpr, int defval)
+                                const char *name, int gpr, int defval)
 {
        ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
        strcpy(ctl->id.name, name);
        ctl->vcount = ctl->count = 2;
        if (high_res_gpr_volume) {
-               ctl->min = 0;
+               ctl->min = -1;
                ctl->max = 0x7fffffff;
                ctl->tlv = snd_emu10k1_db_linear;
-               ctl->translation = EMU10K1_GPR_TRANSLATION_NONE;
-               defval = defval * 0x7fffffffLL / 100;
+               ctl->translation = EMU10K1_GPR_TRANSLATION_NEGATE;
+               defval = defval * 0x80000000LL / 100 - 1;
        } else {
                ctl->min = 0;
                ctl->max = 100;
                ctl->tlv = snd_emu10k1_db_scale1;
-               ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+               ctl->translation = EMU10K1_GPR_TRANSLATION_NEG_TABLE100;
        }
        ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
        ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
@@ -1293,36 +1302,36 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
 
 #if 1
        /* PCM front Playback Volume (independent from stereo mix)
-        * playback = 0 + ( gpr * FXBUS_PCM_LEFT_FRONT >> 31)
-        * where gpr contains attenuation from corresponding mixer control
+        * playback = -gpr * FXBUS_PCM_LEFT_FRONT >> 31
+        * where gpr contains negated attenuation from corresponding mixer control
         * (snd_emu10k1_init_stereo_control)
         */
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
        snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
        gpr += 2;
 
        /* PCM Surround Playback (independent from stereo mix) */
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
        snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Surround Playback Volume", gpr, 100);
        gpr += 2;
        
        /* PCM Side Playback (independent from stereo mix) */
        if (emu->card_capabilities->spk71) {
-               A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
-               A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
+               A_OP(icode, &ptr, iMAC1, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
+               A_OP(icode, &ptr, iMAC1, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
                snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Side Playback Volume", gpr, 100);
                gpr += 2;
        }
 
        /* PCM Center Playback (independent from stereo mix) */
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER));
        snd_emu10k1_init_mono_control(&controls[nctl++], "PCM Center Playback Volume", gpr, 100);
        gpr++;
 
        /* PCM LFE Playback (independent from stereo mix) */
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE));
        snd_emu10k1_init_mono_control(&controls[nctl++], "PCM LFE Playback Volume", gpr, 100);
        gpr++;
        
@@ -1330,26 +1339,26 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
         * Stereo Mix
         */
        /* Wave (PCM) Playback Volume (will be renamed later) */
-       A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
-       A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+       A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+       A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
        snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100);
        gpr += 2;
 
        /* Synth Playback */
-       A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+0), A_GPR(stereo_mix+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
-       A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_GPR(stereo_mix+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+       A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+0), A_GPR(stereo_mix+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+       A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+1), A_GPR(stereo_mix+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
        snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Playback Volume", gpr, 100);
        gpr += 2;
 
        /* Wave (PCM) Capture */
-       A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
-       A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+       A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+       A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
        snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Capture Volume", gpr, 0);
        gpr += 2;
 
        /* Synth Capture */
-       A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
-       A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+       A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+       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;
       
@@ -1357,23 +1366,23 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
         * inputs
         */
 #define A_ADD_VOLUME_IN(var,vol,input) \
-A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
+       A_OP(icode, &ptr, iMAC1, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
 
        if (emu->card_capabilities->emu_model) {
                /* EMU1010 DSP 0 and DSP 1 Capture */
                // The 24 MSB hold the actual value. We implicitly discard the 16 LSB.
                if (emu->card_capabilities->ca0108_chip) {
                        // For unclear reasons, the EMU32IN cannot be the Y operand!
-                       A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A3_EMU32IN(0x0), A_GPR(gpr));
+                       A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A3_EMU32IN(0x0), A_GPR(gpr));
                        // A3_EMU32IN(0) is delayed by one sample, so all other A3_EMU32IN channels
                        // need to be delayed as well; we use an auxiliary register for that.
-                       A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+2), A_GPR(gpr+1));
+                       A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+2), A_GPR(gpr+1));
                        A_OP(icode, &ptr, iACC3, A_GPR(gpr+2), A3_EMU32IN(0x1), A_C_00000000, A_C_00000000);
                } else {
-                       A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_P16VIN(0x0));
+                       A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A_P16VIN(0x0), A_GPR(gpr));
                        // A_P16VIN(0) is delayed by one sample, so all other A_P16VIN channels
                        // need to be delayed as well; we use an auxiliary register for that.
-                       A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_GPR(gpr+2));
+                       A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+2), A_GPR(gpr+1));
                        A_OP(icode, &ptr, iACC3, A_GPR(gpr+2), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
                }
                snd_emu10k1_init_stereo_control(&controls[nctl++], "EMU Capture Volume", gpr, 0);
@@ -1465,33 +1474,33 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
        }
        
        /* Stereo Mix Front Playback Volume */
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_GPR(playback), A_GPR(gpr), A_GPR(stereo_mix));
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback), A_GPR(playback), A_GPR(gpr), A_GPR(stereo_mix));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_GPR(stereo_mix+1));
        snd_emu10k1_init_stereo_control(&controls[nctl++], "Front Playback Volume", gpr, 100);
        gpr += 2;
        
        /* Stereo Mix Surround Playback */
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_GPR(playback+2), A_GPR(gpr), A_GPR(stereo_mix));
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_GPR(playback+3), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback+2), A_GPR(playback+2), A_GPR(gpr), A_GPR(stereo_mix));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback+3), A_GPR(playback+3), A_GPR(gpr+1), A_GPR(stereo_mix+1));
        snd_emu10k1_init_stereo_control(&controls[nctl++], "Surround Playback Volume", gpr, 0);
        gpr += 2;
 
        /* Stereo Mix Center Playback */
        /* Center = sub = Left/2 + Right/2 */
        A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_GPR(stereo_mix), A_C_40000000, A_GPR(stereo_mix+1));
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_GPR(tmp));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_GPR(tmp));
        snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 0);
        gpr++;
 
        /* Stereo Mix LFE Playback */
-       A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_GPR(tmp));
+       A_OP(icode, &ptr, iMAC1, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_GPR(tmp));
        snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0);
        gpr++;
        
        if (emu->card_capabilities->spk71) {
                /* Stereo Mix Side Playback */
-               A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
-               A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+               A_OP(icode, &ptr, iMAC1, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
+               A_OP(icode, &ptr, iMAC1, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
                snd_emu10k1_init_stereo_control(&controls[nctl++], "Side Playback Volume", gpr, 0);
                gpr += 2;
        }
@@ -1579,7 +1588,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
 
        /* Master volume (will be renamed later) */
        for (z = 0; z < 8; z++)
-               A_OP(icode, &ptr, iMAC0, A_GPR(playback+z), A_C_00000000, A_GPR(gpr), A_GPR(playback+z));
+               A_OP(icode, &ptr, iMAC1, A_GPR(playback+z), A_C_00000000, A_GPR(gpr), A_GPR(playback+z));
        snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Master Playback Volume", gpr, 0);
        gpr++;
 
@@ -1731,30 +1740,14 @@ __err_gpr:
  * initial DSP configuration for Emu10k1
  */
 
-/* when volume = max, then copy only to avoid volume modification */
-/* with iMAC0 (negative values) */
+/* Volumes are in the [-2^31, 0] range, zero being mute. */
 static void _volume(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
 {
-       OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
-       OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
-       OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000001);
-       OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
+       OP(icode, ptr, iMAC1, dst, C_00000000, src, vol);
 }
 static void _volume_add(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
 {
-       OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
-       OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
-       OP(icode, ptr, iMACINT0, dst, dst, src, C_00000001);
-       OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
-       OP(icode, ptr, iMAC0, dst, dst, src, vol);
-}
-static void _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
-{
-       OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
-       OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
-       OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
-       OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
-       OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
+       OP(icode, ptr, iMAC1, dst, dst, src, vol);
 }
 
 #define VOLUME(icode, ptr, dst, src, vol) \
@@ -1766,7 +1759,7 @@ static void _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst
 #define VOLUME_ADDIN(icode, ptr, dst, src, vol) \
                _volume_add(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
 #define VOLUME_OUT(icode, ptr, dst, src, vol) \
-               _volume_out(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol))
+               _volume(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol))
 #define _SWITCH(icode, ptr, dst, src, sw) \
        OP((icode), ptr, iMACINT0, dst, C_00000000, src, sw);
 #define SWITCH(icode, ptr, dst, src, sw) \