[PATCH v2 1/6] winealsa: Add a temporary write_best_effort syscall.

Andrew Eikum aeikum at codeweavers.com
Thu Mar 3 12:56:18 CST 2022


Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>

On Wed, Mar 02, 2022 at 10:53:15AM +0000, Huw Davies wrote:
> This will allow the timing loop to move over without needing
> to move "start" at the same time.
> 
> Signed-off-by: Huw Davies <huw at codeweavers.com>
> ---
>  dlls/winealsa.drv/alsa.c     | 237 ++++++++++++++++++++++++++++++++++-
>  dlls/winealsa.drv/mmdevdrv.c | 230 +--------------------------------
>  dlls/winealsa.drv/unixlib.h  |  10 ++
>  3 files changed, 252 insertions(+), 225 deletions(-)
> 
> diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c
> index c73490ebfe6..73971c0f8bb 100644
> --- a/dlls/winealsa.drv/alsa.c
> +++ b/dlls/winealsa.drv/alsa.c
> @@ -970,7 +970,7 @@ static NTSTATUS release_stream(void *args)
>          size = 0;
>          NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, &size, MEM_RELEASE);
>      }
> -    /* free(stream->remapping_buf); */
> +    free(stream->remapping_buf);
>      free(stream->silence_buf);
>      free(stream->hw_params);
>      free(stream->fmt);
> @@ -982,6 +982,239 @@ static NTSTATUS release_stream(void *args)
>      return STATUS_SUCCESS;
>  }
>  
> +static BYTE *remap_channels(struct alsa_stream *stream, BYTE *buf, snd_pcm_uframes_t frames)
> +{
> +    snd_pcm_uframes_t i;
> +    UINT c;
> +    UINT bytes_per_sample = stream->fmt->wBitsPerSample / 8;
> +
> +    if(!stream->need_remapping)
> +        return buf;
> +
> +    if(stream->remapping_buf_frames < frames){
> +        stream->remapping_buf = realloc(stream->remapping_buf,
> +                                        bytes_per_sample * stream->alsa_channels * frames);
> +        stream->remapping_buf_frames = frames;
> +    }
> +
> +    snd_pcm_format_set_silence(stream->alsa_format, stream->remapping_buf,
> +            frames * stream->alsa_channels);
> +
> +    switch(stream->fmt->wBitsPerSample){
> +    case 8: {
> +            UINT8 *tgt_buf, *src_buf;
> +            tgt_buf = stream->remapping_buf;
> +            src_buf = buf;
> +            for(i = 0; i < frames; ++i){
> +                for(c = 0; c < stream->fmt->nChannels; ++c)
> +                    tgt_buf[stream->alsa_channel_map[c]] = src_buf[c];
> +                tgt_buf += stream->alsa_channels;
> +                src_buf += stream->fmt->nChannels;
> +            }
> +            break;
> +        }
> +    case 16: {
> +            UINT16 *tgt_buf, *src_buf;
> +            tgt_buf = (UINT16*)stream->remapping_buf;
> +            src_buf = (UINT16*)buf;
> +            for(i = 0; i < frames; ++i){
> +                for(c = 0; c < stream->fmt->nChannels; ++c)
> +                    tgt_buf[stream->alsa_channel_map[c]] = src_buf[c];
> +                tgt_buf += stream->alsa_channels;
> +                src_buf += stream->fmt->nChannels;
> +            }
> +        }
> +        break;
> +    case 32: {
> +            UINT32 *tgt_buf, *src_buf;
> +            tgt_buf = (UINT32*)stream->remapping_buf;
> +            src_buf = (UINT32*)buf;
> +            for(i = 0; i < frames; ++i){
> +                for(c = 0; c < stream->fmt->nChannels; ++c)
> +                    tgt_buf[stream->alsa_channel_map[c]] = src_buf[c];
> +                tgt_buf += stream->alsa_channels;
> +                src_buf += stream->fmt->nChannels;
> +            }
> +        }
> +        break;
> +    default: {
> +            BYTE *tgt_buf, *src_buf;
> +            tgt_buf = stream->remapping_buf;
> +            src_buf = buf;
> +            for(i = 0; i < frames; ++i){
> +                for(c = 0; c < stream->fmt->nChannels; ++c)
> +                    memcpy(&tgt_buf[stream->alsa_channel_map[c] * bytes_per_sample],
> +                            &src_buf[c * bytes_per_sample], bytes_per_sample);
> +                tgt_buf += stream->alsa_channels * bytes_per_sample;
> +                src_buf += stream->fmt->nChannels * bytes_per_sample;
> +            }
> +        }
> +        break;
> +    }
> +
> +    return stream->remapping_buf;
> +}
> +
> +static void adjust_buffer_volume(const struct alsa_stream *stream, BYTE *buf, snd_pcm_uframes_t frames)
> +{
> +    BOOL adjust = FALSE;
> +    UINT32 i, channels, mute = 0;
> +    BYTE *end;
> +
> +    if (stream->vol_adjusted_frames >= frames)
> +        return;
> +    channels = stream->fmt->nChannels;
> +
> +    /* Adjust the buffer based on the volume for each channel */
> +    for (i = 0; i < channels; i++)
> +    {
> +        adjust |= stream->vols[i] != 1.0f;
> +        if (stream->vols[i] == 0.0f)
> +            mute++;
> +    }
> +
> +    if (mute == channels)
> +    {
> +        int err = snd_pcm_format_set_silence(stream->alsa_format, buf, frames * channels);
> +        if (err < 0)
> +            WARN("Setting buffer to silence failed: %d (%s)\n", err, snd_strerror(err));
> +        return;
> +    }
> +    if (!adjust) return;
> +
> +    /* Skip the frames we've already adjusted before */
> +    end = buf + frames * stream->fmt->nBlockAlign;
> +    buf += stream->vol_adjusted_frames * stream->fmt->nBlockAlign;
> +
> +    switch (stream->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] * stream->vols[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 (stream->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] * stream->vols[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) * stream->vols[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) * stream->vols[i]) + 128;
> +            p += i;
> +        } while ((BYTE*)p != end);
> +        break;
> +    }
> +    default:
> +        TRACE("Unhandled format %i, not adjusting volume.\n", stream->alsa_format);
> +        break;
> +    }
> +}
> +
> +static snd_pcm_sframes_t alsa_write_best_effort(struct alsa_stream *stream, BYTE *buf, snd_pcm_uframes_t frames)
> +{
> +    snd_pcm_sframes_t written;
> +
> +    adjust_buffer_volume(stream, buf, frames);
> +
> +    /* Mark the frames we've already adjusted */
> +    if (stream->vol_adjusted_frames < frames)
> +        stream->vol_adjusted_frames = frames;
> +
> +    buf = remap_channels(stream, buf, frames);
> +
> +    written = snd_pcm_writei(stream->pcm_handle, buf, frames);
> +    if(written < 0){
> +        int ret;
> +
> +        if(written == -EAGAIN)
> +            /* buffer full */
> +            return 0;
> +
> +        WARN("writei failed, recovering: %ld (%s)\n", written,
> +                snd_strerror(written));
> +
> +        ret = snd_pcm_recover(stream->pcm_handle, written, 0);
> +        if(ret < 0){
> +            WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
> +            return ret;
> +        }
> +
> +        written = snd_pcm_writei(stream->pcm_handle, buf, frames);
> +    }
> +
> +    if (written > 0)
> +        stream->vol_adjusted_frames -= written;
> +    return written;
> +}
> +
> +static NTSTATUS write_best_effort(void *args)
> +{
> +    struct write_best_effort_tmp_params *params = args;
> +    *params->written = alsa_write_best_effort(params->stream, params->buf, params->frames);
> +    return STATUS_SUCCESS;
> +}
> +
>  static NTSTATUS is_format_supported(void *args)
>  {
>      struct is_format_supported_params *params = args;
> @@ -1294,4 +1527,6 @@ unixlib_entry_t __wine_unix_call_funcs[] =
>      get_buffer_size,
>      get_latency,
>      get_current_padding,
> +
> +    write_best_effort /* temporary */
>  };
> diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
> index a4ab0e179d1..c06b08709f2 100644
> --- a/dlls/winealsa.drv/mmdevdrv.c
> +++ b/dlls/winealsa.drv/mmdevdrv.c
> @@ -256,9 +256,6 @@ static HRESULT alsa_stream_release(struct alsa_stream *stream)
>  {
>      struct release_stream_params params;
>  
> -    /* FIXME: to be moved with remap_channels() */
> -    HeapFree(GetProcessHeap(), 0, stream->remapping_buf);
> -
>      params.stream = stream;
>  
>      ALSA_CALL(release_stream, &params);
> @@ -969,233 +966,18 @@ static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
>      return S_OK;
>  }
>  
> -static BYTE *remap_channels(struct alsa_stream *stream, BYTE *buf, snd_pcm_uframes_t frames)
> -{
> -    snd_pcm_uframes_t i;
> -    UINT c;
> -    UINT bytes_per_sample = stream->fmt->wBitsPerSample / 8;
> -
> -    if(!stream->need_remapping)
> -        return buf;
> -
> -    if(!stream->remapping_buf){
> -        stream->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
> -                bytes_per_sample * stream->alsa_channels * frames);
> -        stream->remapping_buf_frames = frames;
> -    }else if(stream->remapping_buf_frames < frames){
> -        stream->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, stream->remapping_buf,
> -                bytes_per_sample * stream->alsa_channels * frames);
> -        stream->remapping_buf_frames = frames;
> -    }
> -
> -    snd_pcm_format_set_silence(stream->alsa_format, stream->remapping_buf,
> -            frames * stream->alsa_channels);
> -
> -    switch(stream->fmt->wBitsPerSample){
> -    case 8: {
> -            UINT8 *tgt_buf, *src_buf;
> -            tgt_buf = stream->remapping_buf;
> -            src_buf = buf;
> -            for(i = 0; i < frames; ++i){
> -                for(c = 0; c < stream->fmt->nChannels; ++c)
> -                    tgt_buf[stream->alsa_channel_map[c]] = src_buf[c];
> -                tgt_buf += stream->alsa_channels;
> -                src_buf += stream->fmt->nChannels;
> -            }
> -            break;
> -        }
> -    case 16: {
> -            UINT16 *tgt_buf, *src_buf;
> -            tgt_buf = (UINT16*)stream->remapping_buf;
> -            src_buf = (UINT16*)buf;
> -            for(i = 0; i < frames; ++i){
> -                for(c = 0; c < stream->fmt->nChannels; ++c)
> -                    tgt_buf[stream->alsa_channel_map[c]] = src_buf[c];
> -                tgt_buf += stream->alsa_channels;
> -                src_buf += stream->fmt->nChannels;
> -            }
> -        }
> -        break;
> -    case 32: {
> -            UINT32 *tgt_buf, *src_buf;
> -            tgt_buf = (UINT32*)stream->remapping_buf;
> -            src_buf = (UINT32*)buf;
> -            for(i = 0; i < frames; ++i){
> -                for(c = 0; c < stream->fmt->nChannels; ++c)
> -                    tgt_buf[stream->alsa_channel_map[c]] = src_buf[c];
> -                tgt_buf += stream->alsa_channels;
> -                src_buf += stream->fmt->nChannels;
> -            }
> -        }
> -        break;
> -    default: {
> -            BYTE *tgt_buf, *src_buf;
> -            tgt_buf = stream->remapping_buf;
> -            src_buf = buf;
> -            for(i = 0; i < frames; ++i){
> -                for(c = 0; c < stream->fmt->nChannels; ++c)
> -                    memcpy(&tgt_buf[stream->alsa_channel_map[c] * bytes_per_sample],
> -                            &src_buf[c * bytes_per_sample], bytes_per_sample);
> -                tgt_buf += stream->alsa_channels * bytes_per_sample;
> -                src_buf += stream->fmt->nChannels * bytes_per_sample;
> -            }
> -        }
> -        break;
> -    }
> -
> -    return stream->remapping_buf;
> -}
> -
> -static void adjust_buffer_volume(const struct alsa_stream *stream, BYTE *buf, snd_pcm_uframes_t frames)
> -{
> -    BOOL adjust = FALSE;
> -    UINT32 i, channels, mute = 0;
> -    BYTE *end;
> -
> -    if (stream->vol_adjusted_frames >= frames)
> -        return;
> -    channels = stream->fmt->nChannels;
> -
> -    /* Adjust the buffer based on the volume for each channel */
> -    for (i = 0; i < channels; i++)
> -    {
> -        adjust |= stream->vols[i] != 1.0f;
> -        if (stream->vols[i] == 0.0f)
> -            mute++;
> -    }
> -
> -    if (mute == channels)
> -    {
> -        int err = snd_pcm_format_set_silence(stream->alsa_format, buf, frames * channels);
> -        if (err < 0)
> -            WARN("Setting buffer to silence failed: %d (%s)\n", err, snd_strerror(err));
> -        return;
> -    }
> -    if (!adjust) return;
> -
> -    /* Skip the frames we've already adjusted before */
> -    end = buf + frames * stream->fmt->nBlockAlign;
> -    buf += stream->vol_adjusted_frames * stream->fmt->nBlockAlign;
> -
> -    switch (stream->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] * stream->vols[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 (stream->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] * stream->vols[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) * stream->vols[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) * stream->vols[i]) + 128;
> -            p += i;
> -        } while ((BYTE*)p != end);
> -        break;
> -    }
> -    default:
> -        TRACE("Unhandled format %i, not adjusting volume.\n", stream->alsa_format);
> -        break;
> -    }
> -}
> -
>  static snd_pcm_sframes_t alsa_write_best_effort(struct alsa_stream *stream, BYTE *buf, snd_pcm_uframes_t frames)
>  {
> +    struct write_best_effort_tmp_params params;
>      snd_pcm_sframes_t written;
>  
> -    adjust_buffer_volume(stream, buf, frames);
> -
> -    /* Mark the frames we've already adjusted */
> -    if (stream->vol_adjusted_frames < frames)
> -        stream->vol_adjusted_frames = frames;
> -
> -    buf = remap_channels(stream, buf, frames);
> -
> -    written = snd_pcm_writei(stream->pcm_handle, buf, frames);
> -    if(written < 0){
> -        int ret;
> -
> -        if(written == -EAGAIN)
> -            /* buffer full */
> -            return 0;
> -
> -        WARN("writei failed, recovering: %ld (%s)\n", written,
> -                snd_strerror(written));
> -
> -        ret = snd_pcm_recover(stream->pcm_handle, written, 0);
> -        if(ret < 0){
> -            WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
> -            return ret;
> -        }
> +    params.stream = stream;
> +    params.buf = buf;
> +    params.frames = frames;
> +    params.written = &written;
>  
> -        written = snd_pcm_writei(stream->pcm_handle, buf, frames);
> -    }
> +    ALSA_CALL(write_best_effort_tmp, &params);
>  
> -    if (written > 0)
> -        stream->vol_adjusted_frames -= written;
>      return written;
>  }
>  
> diff --git a/dlls/winealsa.drv/unixlib.h b/dlls/winealsa.drv/unixlib.h
> index 2484fca8c90..3fd6d2a73ee 100644
> --- a/dlls/winealsa.drv/unixlib.h
> +++ b/dlls/winealsa.drv/unixlib.h
> @@ -129,6 +129,14 @@ struct get_current_padding_params
>      UINT32 *padding;
>  };
>  
> +struct write_best_effort_tmp_params
> +{
> +    struct alsa_stream *stream;
> +    BYTE *buf;
> +    snd_pcm_uframes_t frames;
> +    snd_pcm_sframes_t *written;
> +};
> +
>  enum alsa_funcs
>  {
>      alsa_get_endpoint_ids,
> @@ -139,6 +147,8 @@ enum alsa_funcs
>      alsa_get_buffer_size,
>      alsa_get_latency,
>      alsa_get_current_padding,
> +
> +    alsa_write_best_effort_tmp
>  };
>  
>  extern unixlib_handle_t alsa_handle;
> -- 
> 2.25.1
> 
> 



More information about the wine-devel mailing list