[RFC/PATCH] Bad volume scaling with Win7 guest, spice audio, and Qemu Intel HDA codec

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

I see a problem with input volume control on a Windows7 guest
using the qemu Intel HDA codec. In short, moving the volume slider for
the input volume from 0% to 1% under Windows results in 
the "gain" values in the emulated HW to jump from 0 to 40 (out of 74)
(looking at "st->gain_[left|right]" in hda_audio_command()). This makes
it impossible to control sound volume in the low-gain range using the
windows guest.

Here is a table of Windows slider position vs. actual gain value to
illustrate the problem ("0.5" is a slider position between 0 and 1 that
can be reached with skillful slider shifting, windows still displays the
numerical value "0"):

        slider  gain
        0.5     34
        1       40
        2       43
        3       46
        5       48
        10      54
        20      60
        50      68
        75      71
        89      73
        99      73
        100     74

For output, the situation is similar but less (1% on Win7 corresponds to
a gain value of 19/74).

I am using the spice driver connected to pulseaudio to control the
volume in the host (PA adds another level of complication which I am
going to address in a posting to pulseaudio-discuss soon). The spice-



I am using a Win7(32bit) guest on a Fedora 21 host.
  qemu-system-x86-2.1.3-6.fc21.x86_64
  spice-gtk3-0.27-6.fc21.x86_64
  pulseaudio-6.0-2.fc21.1.x86_64

By using low-level controls in a Linux VM (e.g. "amixer -c 0 set Capture
$x), I was able to set the gain levels to any value $x between 0 and 74,
just as one would expect. So this is not a problem of QEMU alone.
Rather, it's related to how the Windows HDA codec driver and the QEMU
emulated HW interact. For the Windows side I can only guess, but it
seems that Windows uses a ~30dB overall scale for the min-max range of
the input volume slider (and ~60dB for output). I'm not sure if that's
general Win7 behavior or related to the driver. According to
https://msdn.microsoft.com/en-us/library/windows/hardware/ff536251%
28v=vs.85%29.aspx, audio drivers can set volume-related registry values,
but I didn't find any of those on my system.

I experimentally changed the value of QEMU_HDA_AMP_STEPS in
hw/audio/hda-codec.c from 74 to 32, artificially reducing the dynamic
range of the HDA codec to 32 dB. This improved matters much, allowing me
to set a gain value as low as 4 (-28dB / 12.5%) from the Windows guest
at 1%. Going one step further and using 32dB dynamic range with a 0.5 dB
step size, I could even set a gain value of 5 (-29.5dB / 8%).

The "right" solution for this problem would probably be to implement
proper dB scaling in the the spice client code, as the note in the git
commit suggests
(http://cgit.freedesktop.org/spice/spice/commit/server/snd_worker.c?id=d1758b328811979beb58ff9ddb9cf4f318fa28f7).
I am not sure how difficult this might be. AFAICS it would require
changes in the general QEMU audio API to deal with dB.

While this clean solution is not available, I would suggest to decrease
the dynamic range for the the emulated Amps in the QEMU hda codec code.
AFAICS, that would also match the dynamic range of actual HD Audio HW
better. I am attaching a tentative patch that does exactly that for
input, changing nothing for output (the output volume setting behaves
acceptably for the default QEMU settings, see above). Please comment.

Regards
Martin
--- qemu-2.1.3/hw/audio/hda-codec-common.h	2015-04-20 14:44:18.621197997 +0200
+++ qemu-2.1.3/hw/audio/hda-codec-common.h.new	2015-04-20 14:43:24.852627519 +0200
@@ -28,16 +28,22 @@
 #define QEMU_HDA_ID_OUTPUT  ((QEMU_HDA_ID_VENDOR << 16) | 0x12)
 #define QEMU_HDA_ID_DUPLEX  ((QEMU_HDA_ID_VENDOR << 16) | 0x22)
 #define QEMU_HDA_ID_MICRO   ((QEMU_HDA_ID_VENDOR << 16) | 0x32)
-#define QEMU_HDA_AMP_CAPS                                               \
+#define QEMU_HDA_AMP_OUT_CAPS						\
     (AC_AMPCAP_MUTE |                                                   \
-     (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT)    |                \
-     (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) |                \
-     (3                  << AC_AMPCAP_STEP_SIZE_SHIFT))
+     (QEMU_HDA_AMP_OUT_STEPS << AC_AMPCAP_OFFSET_SHIFT)    |            \
+     (QEMU_HDA_AMP_OUT_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) |            \
+     (QEMU_HDA_AMP_OUT_STEP_SIZE << AC_AMPCAP_STEP_SIZE_SHIFT))
+#define QEMU_HDA_AMP_IN_CAPS						\
+    (AC_AMPCAP_MUTE |                                                   \
+     (QEMU_HDA_AMP_IN_STEPS << AC_AMPCAP_OFFSET_SHIFT)    |             \
+     (QEMU_HDA_AMP_IN_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) |             \
+     (QEMU_HDA_AMP_IN_STEP_SIZE << AC_AMPCAP_STEP_SIZE_SHIFT))
 #else
 #define QEMU_HDA_ID_OUTPUT  ((QEMU_HDA_ID_VENDOR << 16) | 0x11)
 #define QEMU_HDA_ID_DUPLEX  ((QEMU_HDA_ID_VENDOR << 16) | 0x21)
 #define QEMU_HDA_ID_MICRO   ((QEMU_HDA_ID_VENDOR << 16) | 0x31)
-#define QEMU_HDA_AMP_CAPS   QEMU_HDA_AMP_NONE
+#define QEMU_HDA_AMP_OUT_CAPS   QEMU_HDA_AMP_NONE
+#define QEMU_HDA_AMP_IN_CAPS   QEMU_HDA_AMP_NONE
 #endif
 
 
@@ -61,7 +67,7 @@ static const desc_param glue(common_para
         .val = QEMU_HDA_AMP_NONE,
     },{
         .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_CAPS,
+        .val = QEMU_HDA_AMP_OUT_CAPS,
     },
 };
 
@@ -86,7 +92,7 @@ static const desc_param glue(common_para
         .val = AC_SUPFMT_PCM,
     },{
         .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_CAPS,
+        .val = QEMU_HDA_AMP_IN_CAPS,
     },{
         .id  = AC_PAR_AMP_OUT_CAP,
         .val = QEMU_HDA_AMP_NONE,
@@ -453,4 +459,5 @@ static const desc_codec glue(micro_, PAR
 #undef QEMU_HDA_ID_OUTPUT
 #undef QEMU_HDA_ID_DUPLEX
 #undef QEMU_HDA_ID_MICRO
-#undef QEMU_HDA_AMP_CAPS
+#undef QEMU_HDA_AMP_IN_CAPS
+#undef QEMU_HDA_AMP_OUT_CAPS
--- qemu-2.1.3/hw/audio/hda-codec.c	2015-04-20 15:20:38.069019517 +0200
+++ qemu-2.1.3/hw/audio/hda-codec.c.new	2015-04-20 15:17:32.443070187 +0200
@@ -116,7 +116,17 @@
 #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
+/* Amplifier properties */
+/* Step size: 0: 0.25dB 1: 0.5dB 2: 0.75dB 3: 1dB */
+/* 
+   Until we have a way to propagate dB values cleanly to the host
+   e.g. through spice, it is better to use a smaller dynamic range
+   for input. Here we use 0x40 * 0.5 dB = 32dB.
+*/
+#define QEMU_HDA_AMP_OUT_STEPS   0x4a
+#define QEMU_HDA_AMP_OUT_STEP_SIZE  3
+#define QEMU_HDA_AMP_IN_STEPS    0x40
+#define QEMU_HDA_AMP_IN_STEP_SIZE   1
 
 #define   PARAM mixemu
 #define   HDA_MIXER
@@ -258,15 +268,16 @@
     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;
-
     if (!st->state->mixer) {
         return;
     }
     if (st->output) {
+        left = left * 255 / QEMU_HDA_AMP_OUT_STEPS;
+        right = right * 255 / QEMU_HDA_AMP_OUT_STEPS;
         AUD_set_volume_out(st->voice.out, muted, left, right);
     } else {
+        left = left * 255 / QEMU_HDA_AMP_IN_STEPS;
+        right = right * 255 / QEMU_HDA_AMP_IN_STEPS;
         AUD_set_volume_in(st->voice.in, muted, left, right);
     }
 }
@@ -512,8 +512,8 @@
             st->node = node;
             if (type == AC_WID_AUD_OUT) {
                 /* unmute output by default */
-                st->gain_left = QEMU_HDA_AMP_STEPS;
-                st->gain_right = QEMU_HDA_AMP_STEPS;
+                st->gain_left = QEMU_HDA_AMP_OUT_STEPS;
+                st->gain_right = QEMU_HDA_AMP_OUT_STEPS;
                 st->bpos = sizeof(st->buf);
                 st->output = true;
             } else {
_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/spice-devel

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]