Gabriel Ivăncescu : winealsa.drv: Adjust the buffer volume before sending it to ALSA.

Alexandre Julliard julliard at winehq.org
Mon Nov 25 09:12:23 CST 2019


Module: wine
Branch: stable
Commit: 5b45b6232ffdfad3c89bea73a6f74fc15e3d094c
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=5b45b6232ffdfad3c89bea73a6f74fc15e3d094c

Author: Gabriel Ivăncescu <gabrielopcode at gmail.com>
Date:   Mon May 20 13:43:36 2019 +0300

winealsa.drv: Adjust the buffer volume before sending it to ALSA.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=38182
Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>
(cherry picked from commit 9d48b3294c0bf73f65ccefae28ed225aaba4abab)
Signed-off-by: Michael Stefaniuc <mstefani at winehq.org>

---

 dlls/winealsa.drv/mmdevdrv.c | 137 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 130 insertions(+), 7 deletions(-)

diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index de89fd05c5..12efde0c27 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -126,6 +126,7 @@ struct ACImpl {
     UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
     UINT32 wri_offs_frames; /* where to write fresh data in local_buffer */
     UINT32 hidden_frames;   /* ALSA reserve to ensure continuous rendering */
+    UINT32 vol_adjusted_frames; /* Frames we've already adjusted the volume of but didn't write yet */
     UINT32 data_in_alsa_frames;
 
     HANDLE timer;
@@ -1971,18 +1972,135 @@ static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
     return This->remapping_buf;
 }
 
+static void adjust_buffer_volume(const ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames, BOOL mute)
+{
+    float vol[ARRAY_SIZE(This->alsa_channel_map)];
+    BOOL adjust = FALSE;
+    UINT32 i, channels;
+    BYTE *end;
+
+    if (This->vol_adjusted_frames >= frames)
+        return;
+    channels = This->fmt->nChannels;
+
+    if (mute)
+    {
+        int err = snd_pcm_format_set_silence(This->alsa_format, buf, frames * channels);
+        if (err < 0)
+            WARN("Setting buffer to silence failed: %d (%s)\n", err, snd_strerror(err));
+        return;
+    }
+
+    /* Adjust the buffer based on the volume for each channel */
+    for (i = 0; i < channels; i++)
+        vol[i] = This->vols[i] * This->session->master_vol;
+    for (i = 0; i < min(channels, This->session->channel_count); i++)
+    {
+        vol[i] *= This->session->channel_vols[i];
+        adjust |= vol[i] != 1.0f;
+    }
+    while (i < channels) adjust |= vol[i++] != 1.0f;
+    if (!adjust) return;
+
+    /* Skip the frames we've already adjusted before */
+    end = buf + frames * This->fmt->nBlockAlign;
+    buf += This->vol_adjusted_frames * This->fmt->nBlockAlign;
+
+    switch (This->alsa_format)
+    {
+#ifndef WORDS_BIGENDIAN
+#define PROCESS_BUFFER(type) do         \
+{                                       \
+    type *p = (type*)buf;               \
+    do                                  \
+    {                                   \
+        for (i = 0; i < channels; i++)  \
+            p[i] = p[i] * vol[i];       \
+        p += i;                         \
+    } while ((BYTE*)p != end);          \
+} while (0)
+    case SND_PCM_FORMAT_S16_LE:
+        PROCESS_BUFFER(INT16);
+        break;
+    case SND_PCM_FORMAT_S32_LE:
+        PROCESS_BUFFER(INT32);
+        break;
+    case SND_PCM_FORMAT_FLOAT_LE:
+        PROCESS_BUFFER(float);
+        break;
+    case SND_PCM_FORMAT_FLOAT64_LE:
+        PROCESS_BUFFER(double);
+        break;
+#undef PROCESS_BUFFER
+    case SND_PCM_FORMAT_S20_3LE:
+    case SND_PCM_FORMAT_S24_3LE:
+    {
+        /* Do it 12 bytes at a time until it is no longer possible */
+        UINT32 *q = (UINT32*)buf, mask = ~0xff;
+        BYTE *p;
+
+        /* After we adjust the volume, we need to mask out low bits */
+        if (This->alsa_format == SND_PCM_FORMAT_S20_3LE)
+            mask = ~0x0fff;
+
+        i = 0;
+        while (end - (BYTE*)q >= 12)
+        {
+            UINT32 v[4], k;
+            v[0] = q[0] << 8;
+            v[1] = q[1] << 16 | (q[0] >> 16 & ~0xff);
+            v[2] = q[2] << 24 | (q[1] >> 8  & ~0xff);
+            v[3] = q[2] & ~0xff;
+            for (k = 0; k < 4; k++)
+            {
+                v[k] = (INT32)((INT32)v[k] * vol[i]);
+                v[k] &= mask;
+                if (++i == channels) i = 0;
+            }
+            *q++ = v[0] >> 8  | v[1] << 16;
+            *q++ = v[1] >> 16 | v[2] << 8;
+            *q++ = v[2] >> 24 | v[3];
+        }
+        p = (BYTE*)q;
+        while (p != end)
+        {
+            UINT32 v = (INT32)((INT32)(p[0] << 8 | p[1] << 16 | p[2] << 24) * vol[i]);
+            v &= mask;
+            *p++ = v >> 8  & 0xff;
+            *p++ = v >> 16 & 0xff;
+            *p++ = v >> 24;
+            if (++i == channels) i = 0;
+        }
+        break;
+    }
+#endif
+    case SND_PCM_FORMAT_U8:
+    {
+        UINT8 *p = (UINT8*)buf;
+        do
+        {
+            for (i = 0; i < channels; i++)
+                p[i] = (int)((p[i] - 128) * vol[i]) + 128;
+            p += i;
+        } while ((BYTE*)p != end);
+        break;
+    }
+    default:
+        TRACE("Unhandled format %i, not adjusting volume.\n", This->alsa_format);
+        break;
+    }
+}
+
 static snd_pcm_sframes_t alsa_write_best_effort(ACImpl *This, BYTE *buf,
         snd_pcm_uframes_t frames, BOOL mute)
 {
     snd_pcm_sframes_t written;
 
-    if(mute){
-        int err;
-        if((err = snd_pcm_format_set_silence(This->alsa_format, buf,
-                        frames * This->fmt->nChannels)) < 0)
-            WARN("Setting buffer to silence failed: %d (%s)\n", err,
-                    snd_strerror(err));
-    }
+    adjust_buffer_volume(This, buf, frames, mute);
+
+    /* Mark the frames we've already adjusted */
+    if (This->vol_adjusted_frames < frames)
+        This->vol_adjusted_frames = frames;
 
     buf = remap_channels(This, buf, frames);
 
@@ -2006,6 +2124,8 @@ static snd_pcm_sframes_t alsa_write_best_effort(ACImpl *This, BYTE *buf,
         written = snd_pcm_writei(This->pcm_handle, buf, frames);
     }
 
+    if (written > 0)
+        This->vol_adjusted_frames -= written;
     return written;
 }
 
@@ -2103,7 +2223,10 @@ static void alsa_write_data(ACImpl *This)
     /* Add a lead-in when starting with too few frames to ensure
      * continuous rendering.  Additional benefit: Force ALSA to start. */
     if(This->data_in_alsa_frames == 0 && This->held_frames < This->alsa_period_frames)
+    {
         alsa_write_best_effort(This, This->silence_buf, This->alsa_period_frames - This->held_frames, FALSE);
+        This->vol_adjusted_frames = 0;
+    }
 
     if(This->started)
         max_copy_frames = data_not_in_alsa(This);




More information about the wine-cvs mailing list