Commit bcdbd3b7 authored by Oswald Buddenhagen's avatar Oswald Buddenhagen Committed by Takashi Iwai

ALSA: emu10k1: enable bit-exact playback, part 2: voice attenuation

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: default avatarOswald Buddenhagen <oswald.buddenhagen@gmx.de>
Link: https://lore.kernel.org/r/20230514170323.3408834-8-oswald.buddenhagen@gmx.deSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 1298bc97
......@@ -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:
......
......@@ -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:
......
......@@ -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;
};
......
......@@ -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);
......
......@@ -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;
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment