[PATCH] hda-audio: use log-scale for amplifier levels

Yuanqing Li posted 1 patch 9 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/TYCP286MB12667F4B46EE030F93FFDA47ED04A@TYCP286MB1266.JPNP286.PROD.OUTLOOK.COM
Maintainers: Gerd Hoffmann <kraxel@redhat.com>
hw/audio/hda-codec.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
[PATCH] hda-audio: use log-scale for amplifier levels
Posted by Yuanqing Li 9 months ago
According Intel's High Definition Audio Specification (Revision 1.0a,
Section 7.3.4.10: Amplifier Capabilities), the amplifier gain levels
should be evenly spaced in dB, i.e. using a log scale instead of linear.

Here, the hda-codec reports amplifier levels from 0 to -48 dB at 1-dB
steps matching the 8-bit dynamic range, and the -49 dB level is mapped
to 0 (muted).

Signed-off-by: Yuanqing Li <liyq.yuanqing@outlook.com>
---
 hw/audio/hda-codec.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index c51d8ba617..c2aa71624b 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -121,7 +121,7 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
 #define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 |       \
                               0x1fc /* 16 -> 96 kHz */)
 #define QEMU_HDA_AMP_NONE    (0)
-#define QEMU_HDA_AMP_STEPS   0x4a
+#define QEMU_HDA_AMP_STEPS   0x31 /* 20 * log10(255) = 48 dB dynamic range */
 
 #define   PARAM mixemu
 #define   HDA_MIXER
@@ -433,6 +433,14 @@ static void hda_audio_set_running(HDAAudioStream *st, bool running)
     }
 }
 
+/* first muted; then from -48 dB to 0 dB */
+static const uint32_t hda_vol_table[] = {
+    0,   1,   1,   1,   1,   2,   2,   2,   2,   3,   3,   3,   4,   4,   5,
+    5,   6,   6,   7,   8,   9,   10,  11,  13,  14,  16,  18,  20,  23,  26,
+    29,  32,  36,  40,  45,  51,  57,  64,  72,  81,  90,  102, 114, 128, 143,
+    161, 181, 203, 227, 255
+}
+
 static void hda_audio_set_amp(HDAAudioStream *st)
 {
     bool muted;
@@ -446,8 +454,8 @@ static void hda_audio_set_amp(HDAAudioStream *st)
     left  = st->mute_left  ? 0 : st->gain_left;
     right = st->mute_right ? 0 : st->gain_right;
 
-    left = left * 255 / QEMU_HDA_AMP_STEPS;
-    right = right * 255 / QEMU_HDA_AMP_STEPS;
+    left = hda_vol_table[left];
+    right = hda_vol_table[right];
 
     if (!st->state->mixer) {
         return;
-- 
2.41.0
Re: [PATCH] hda-audio: use log-scale for amplifier levels
Posted by Marc-André Lureau 9 months ago
Hi

On Sun, Jul 30, 2023 at 5:52 PM Yuanqing Li <liyq.yuanqing@outlook.com> wrote:
>
> According Intel's High Definition Audio Specification (Revision 1.0a,
> Section 7.3.4.10: Amplifier Capabilities), the amplifier gain levels
> should be evenly spaced in dB, i.e. using a log scale instead of linear.
>
> Here, the hda-codec reports amplifier levels from 0 to -48 dB at 1-dB
> steps matching the 8-bit dynamic range, and the -49 dB level is mapped
> to 0 (muted).
>
> Signed-off-by: Yuanqing Li <liyq.yuanqing@outlook.com>
> ---
>  hw/audio/hda-codec.c | 14 +++++++++++---
>  1 file changed, 11 insertions(+), 3 deletions(-)
>
> diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
> index c51d8ba617..c2aa71624b 100644
> --- a/hw/audio/hda-codec.c
> +++ b/hw/audio/hda-codec.c
> @@ -121,7 +121,7 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
>  #define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 |       \
>                                0x1fc /* 16 -> 96 kHz */)
>  #define QEMU_HDA_AMP_NONE    (0)
> -#define QEMU_HDA_AMP_STEPS   0x4a
> +#define QEMU_HDA_AMP_STEPS   0x31 /* 20 * log10(255) = 48 dB dynamic range */
>
>  #define   PARAM mixemu
>  #define   HDA_MIXER
> @@ -433,6 +433,14 @@ static void hda_audio_set_running(HDAAudioStream *st, bool running)
>      }
>  }
>
> +/* first muted; then from -48 dB to 0 dB */
> +static const uint32_t hda_vol_table[] = {
> +    0,   1,   1,   1,   1,   2,   2,   2,   2,   3,   3,   3,   4,   4,   5,
> +    5,   6,   6,   7,   8,   9,   10,  11,  13,  14,  16,  18,  20,  23,  26,
> +    29,  32,  36,  40,  45,  51,  57,  64,  72,  81,  90,  102, 114, 128, 143,
> +    161, 181, 203, 227, 255
> +}

It was not clear to me which scale is applied by the backend. I guess
mixeng uses linear, and thus all others should too.

> +
>  static void hda_audio_set_amp(HDAAudioStream *st)
>  {
>      bool muted;
> @@ -446,8 +454,8 @@ static void hda_audio_set_amp(HDAAudioStream *st)
>      left  = st->mute_left  ? 0 : st->gain_left;
>      right = st->mute_right ? 0 : st->gain_right;
>
> -    left = left * 255 / QEMU_HDA_AMP_STEPS;
> -    right = right * 255 / QEMU_HDA_AMP_STEPS;
> +    left = hda_vol_table[left];
> +    right = hda_vol_table[right];
>

Whoo, better check bounds here. (value can go up to AC_AMP_GAIN 0x7f,
reading the code)

Definitely not trivial material imho

-- 
Marc-André Lureau