mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
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: 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>
This commit is contained in:
parent
1298bc978a
commit
bcdbd3b788
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user