[PATCH 1/2] winepulse.drv: Adjust the buffer volume before sending it to PulseAudio

Andrew Eikum aeikum at codeweavers.com
Fri May 17 09:07:17 CDT 2019


On Wed, May 08, 2019 at 03:49:15PM +0300, Gabriel Ivăncescu wrote:
> Only fixes the PulseAudio driver for the first bug, not ALSA.
> 
> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=38182

Sorry for the long delay in reviewing this. This crashes the King of
Dragon Pass demo for me. Looks like we're not ending the inner
do-while loop in the U8 case somehow, so it runs past the end of the
buffer.

Andrew

> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46450
> Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
> ---
>  dlls/winepulse.drv/mmdevdrv.c | 154 ++++++++++++++++++++++++++++++----
>  1 file changed, 138 insertions(+), 16 deletions(-)
> 
> diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
> index 8d57f1a..ac53039 100644
> --- a/dlls/winepulse.drv/mmdevdrv.c
> +++ b/dlls/winepulse.drv/mmdevdrv.c
> @@ -621,6 +621,135 @@ static void silence_buffer(pa_sample_format_t format, BYTE *buffer, UINT32 bytes
>      memset(buffer, format == PA_SAMPLE_U8 ? 0x80 : 0, bytes);
>  }
>  
> +static void pulse_free_noop(void *buf)
> +{
> +}
> +
> +enum write_buffer_flags
> +{
> +    WINEPULSE_WRITE_NOFREE = 0x01,
> +    WINEPULSE_WRITE_SILENT = 0x02
> +};
> +
> +static int write_buffer(const ACImpl *This, BYTE *buffer, UINT32 bytes,
> +                        enum write_buffer_flags flags)
> +{
> +    float vol[PA_CHANNELS_MAX];
> +    BOOL adjust = FALSE;
> +    UINT32 i, channels;
> +    BYTE *end;
> +
> +    if (!bytes) return 0;
> +    if (This->session->mute || (flags & WINEPULSE_WRITE_SILENT))
> +    {
> +        silence_buffer(This->ss.format, buffer, bytes);
> +        goto write;
> +    }
> +
> +    /* Adjust the buffer based on the volume for each channel */
> +    channels = This->session->channel_count;
> +    for (i = 0; i < channels; i++)
> +    {
> +        vol[i] = This->vol[i] * This->session->master_vol * This->session->channel_vols[i];
> +        adjust |= vol[i] != 1.0f;
> +    }
> +    if (!adjust) goto write;
> +
> +    end = buffer + bytes;
> +    switch (This->ss.format)
> +    {
> +#ifndef WORDS_BIGENDIAN
> +#define PROCESS_BUFFER(type) do         \
> +{                                       \
> +    type *p = (type*)buffer;            \
> +    do                                  \
> +    {                                   \
> +        for (i = 0; i < channels; i++)  \
> +            p[i] = p[i] * vol[i];       \
> +        p += i;                         \
> +    } while ((BYTE*)p != end);          \
> +} while (0);
> +    case PA_SAMPLE_S16LE:
> +        PROCESS_BUFFER(INT16);
> +        break;
> +    case PA_SAMPLE_S32LE:
> +        PROCESS_BUFFER(INT32);
> +        break;
> +    case PA_SAMPLE_FLOAT32LE:
> +        PROCESS_BUFFER(float);
> +        break;
> +#undef PROCESS_BUFFER
> +    case PA_SAMPLE_S24_32LE:
> +    {
> +        UINT32 *p = (UINT32*)buffer;
> +        do
> +        {
> +            for (i = 0; i < channels; i++)
> +            {
> +                p[i] = (INT32)((INT32)(p[i] << 8) * vol[i]);
> +                p[i] >>= 8;
> +            }
> +            p += i;
> +        } while ((BYTE*)p != end);
> +        break;
> +    }
> +    case PA_SAMPLE_S24LE:
> +    {
> +        /* do it 12 bytes at a time until it is no longer possible */
> +        UINT32 *q = (UINT32*)buffer;
> +        BYTE *p;
> +
> +        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]);
> +                if (++i == channels) i = 0;
> +            }
> +            *q++ = v[0] >> 8  | (v[1] & ~0xff) << 16;
> +            *q++ = v[1] >> 16 | (v[2] & ~0xff) << 8;
> +            *q++ = v[2] >> 24 | (v[3] & ~0xff);
> +        }
> +        p = (BYTE*)q;
> +        while (p != end)
> +        {
> +            UINT32 v = (INT32)((INT32)(p[0] << 8 | p[1] << 16 | p[2] << 24) * vol[i]);
> +            *p++ = v >> 8  & 0xff;
> +            *p++ = v >> 16 & 0xff;
> +            *p++ = v >> 24;
> +            if (++i == channels) i = 0;
> +        }
> +        break;
> +    }
> +#endif
> +    case PA_SAMPLE_U8:
> +    {
> +        UINT8 *p = (UINT8*)buffer;
> +        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->ss.format);
> +        break;
> +    }
> +
> +write:
> +    return pa_stream_write(This->stream, buffer, bytes,
> +                           (flags & WINEPULSE_WRITE_NOFREE) ? pulse_free_noop : NULL,
> +                           0, PA_SEEK_RELATIVE);
> +}
> +
>  static void dump_attr(const pa_buffer_attr *attr) {
>      TRACE("maxlength: %u\n", attr->maxlength);
>      TRACE("minreq: %u\n", attr->minreq);
> @@ -681,7 +810,7 @@ static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
>          if(This->lcl_offs_bytes + bytes > This->bufsize_bytes){
>              to_write = This->bufsize_bytes - This->lcl_offs_bytes;
>              TRACE("writing small chunk of %u bytes\n", to_write);
> -            pa_stream_write(This->stream, buf, to_write, NULL, 0, PA_SEEK_RELATIVE);
> +            write_buffer(This, buf, to_write, 0);
>              This->held_bytes -= to_write;
>              to_write = bytes - to_write;
>              This->lcl_offs_bytes = 0;
> @@ -690,7 +819,7 @@ static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
>              to_write = bytes;
>  
>          TRACE("writing main chunk of %u bytes\n", to_write);
> -        pa_stream_write(This->stream, buf, to_write, NULL, 0, PA_SEEK_RELATIVE);
> +        write_buffer(This, buf, to_write, 0);
>          This->lcl_offs_bytes += to_write;
>          This->lcl_offs_bytes %= This->bufsize_bytes;
>          This->held_bytes -= to_write;
> @@ -2126,10 +2255,6 @@ static void pulse_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_bytes)
>      }
>  }
>  
> -static void pulse_free_noop(void *buf)
> -{
> -}
> -
>  static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
>          IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
>  {
> @@ -2181,7 +2306,7 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
>  
>              TRACE("pre-writing %u bytes\n", to_write);
>  
> -            e = pa_stream_write(This->stream, buffer, to_write, NULL, 0, PA_SEEK_RELATIVE);
> +            e = write_buffer(This, buffer, to_write, 0);
>              if(e)
>                  ERR("pa_stream_write failed: 0x%x\n", e);
>  
> @@ -2191,15 +2316,12 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
>          }
>  
>      }else{
> -        if (This->locked_ptr) {
> -            if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
> -                silence_buffer(This->ss.format, This->locked_ptr, written_bytes);
> -            pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
> -        } else {
> -            if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
> -                silence_buffer(This->ss.format, This->tmp_buffer, written_bytes);
> -            pa_stream_write(This->stream, This->tmp_buffer, written_bytes, pulse_free_noop, 0, PA_SEEK_RELATIVE);
> -        }
> +        enum write_buffer_flags wr_flags = 0;
> +
> +        if (flags & AUDCLNT_BUFFERFLAGS_SILENT) wr_flags |= WINEPULSE_WRITE_SILENT;
> +        if (!This->locked_ptr) wr_flags |= WINEPULSE_WRITE_NOFREE;
> +
> +        write_buffer(This, This->locked_ptr ? This->locked_ptr : This->tmp_buffer, written_bytes, wr_flags);
>          This->pad += written_bytes;
>      }
>  
> -- 
> 2.21.0
> 
> 
> 



More information about the wine-devel mailing list