[PATCH 5/8] wineoss: Move timer_loop to the unixlib.

Andrew Eikum aeikum at codeweavers.com
Wed Apr 13 15:35:01 CDT 2022


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

On Wed, Apr 13, 2022 at 07:38:48AM +0100, Huw Davies wrote:
> Signed-off-by: Huw Davies <huw at codeweavers.com>
> ---
>  dlls/wineoss.drv/mmdevdrv.c | 199 ++++--------------------------------
>  dlls/wineoss.drv/oss.c      | 196 +++++++++++++++++++++++++++++++++++
>  dlls/wineoss.drv/unixlib.h  |   9 +-
>  3 files changed, 223 insertions(+), 181 deletions(-)
> 
> diff --git a/dlls/wineoss.drv/mmdevdrv.c b/dlls/wineoss.drv/mmdevdrv.c
> index e61c3c95df2..8f2b19c8faa 100644
> --- a/dlls/wineoss.drv/mmdevdrv.c
> +++ b/dlls/wineoss.drv/mmdevdrv.c
> @@ -114,7 +114,7 @@ struct ACImpl {
>      UINT32 channel_count;
>      struct oss_stream *stream;
>  
> -    HANDLE timer;
> +    HANDLE timer_thread;
>  
>      AudioSession *session;
>      AudioSessionWrapper *session_wrapper;
> @@ -147,8 +147,6 @@ static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
>      'w','i','n','e','o','s','s','.','d','r','v','\\','d','e','v','i','c','e','s',0};
>  static const WCHAR guidW[] = {'g','u','i','d',0};
>  
> -static HANDLE g_timer_q;
> -
>  static CRITICAL_SECTION g_sessions_lock;
>  static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
>  {
> @@ -230,9 +228,6 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
>          if(NtQueryVirtualMemory(GetCurrentProcess(), dll, MemoryWineUnixFuncs,
>                                  &oss_handle, sizeof(oss_handle), NULL))
>              return FALSE;
> -        g_timer_q = CreateTimerQueue();
> -        if(!g_timer_q)
> -            return FALSE;
>          break;
>  
>      case DLL_PROCESS_DETACH:
> @@ -260,16 +255,28 @@ int WINAPI AUDDRV_GetPriority(void)
>      return params.priority;
>  }
>  
> -static HRESULT stream_release(struct oss_stream *stream)
> +static HRESULT stream_release(struct oss_stream *stream, HANDLE timer_thread)
>  {
>      struct release_stream_params params;
>  
>      params.stream = stream;
> +    params.timer_thread = timer_thread;
>      OSS_CALL(release_stream, &params);
>  
>      return params.result;
>  }
>  
> +static DWORD WINAPI timer_thread(void *user)
> +{
> +    struct oss_stream *stream = user;
> +    struct timer_loop_params params;
> +
> +    params.stream = stream;
> +    OSS_CALL(timer_loop, &params);
> +
> +    return 0;
> +}
> +
>  static void oss_lock(struct oss_stream *stream)
>  {
>      pthread_mutex_lock(&stream->lock);
> @@ -532,17 +539,6 @@ static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
>      ref = InterlockedDecrement(&This->ref);
>      TRACE("(%p) Refcount now %u\n", This, ref);
>      if(!ref){
> -        if(This->timer){
> -            HANDLE event;
> -            DWORD wait;
> -            event = CreateEventW(NULL, TRUE, FALSE, NULL);
> -            wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
> -            wait = wait && GetLastError() == ERROR_IO_PENDING;
> -            if(event && wait)
> -                WaitForSingleObject(event, INFINITE);
> -            CloseHandle(event);
> -        }
> -
>          IAudioClient3_Stop(iface);
>          IMMDevice_Release(This->parent);
>          IUnknown_Release(This->pUnkFTMarshal);
> @@ -553,7 +549,7 @@ static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
>          }
>          HeapFree(GetProcessHeap(), 0, This->vols);
>          if(This->stream)
> -            stream_release(This->stream);
> +            stream_release(This->stream, This->timer_thread);
>          HeapFree(GetProcessHeap(), 0, This);
>      }
>      return ref;
> @@ -765,7 +761,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
>  
>  exit:
>      if(FAILED(params.result)){
> -        stream_release(stream);
> +        stream_release(stream, NULL);
>          HeapFree(GetProcessHeap(), 0, This->vols);
>          This->vols = NULL;
>      } else {
> @@ -935,161 +931,6 @@ static void silence_buffer(struct oss_stream *stream, BYTE *buffer, UINT32 frame
>          memset(buffer, 0, frames * stream->fmt->nBlockAlign);
>  }
>  
> -static void oss_write_data(struct oss_stream *stream)
> -{
> -    ssize_t written_bytes;
> -    UINT32 written_frames, in_oss_frames, write_limit, max_period, write_offs_frames, new_frames;
> -    SIZE_T to_write_frames, to_write_bytes, advanced;
> -    audio_buf_info bi;
> -    BYTE *buf;
> -
> -    if(ioctl(stream->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
> -        WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
> -        return;
> -    }
> -
> -    max_period = max(bi.fragsize / stream->fmt->nBlockAlign, stream->period_frames);
> -
> -    if(bi.bytes > stream->oss_bufsize_bytes){
> -        TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
> -                bi.bytes, stream->oss_bufsize_bytes);
> -        stream->oss_bufsize_bytes = bi.bytes;
> -        in_oss_frames = 0;
> -    }else
> -        in_oss_frames = (stream->oss_bufsize_bytes - bi.bytes) / stream->fmt->nBlockAlign;
> -
> -    if(in_oss_frames > stream->in_oss_frames){
> -        TRACE("Capping reported frames from %u to %u\n",
> -                in_oss_frames, stream->in_oss_frames);
> -        in_oss_frames = stream->in_oss_frames;
> -    }
> -
> -    write_limit = 0;
> -    while(write_limit + in_oss_frames < max_period * 3)
> -        write_limit += max_period;
> -    if(write_limit == 0)
> -        return;
> -
> -    /*        vvvvvv - in_oss_frames
> -     * [--xxxxxxxxxx]
> -     *       [xxxxxxxxxx--]
> -     *        ^^^^^^^^^^ - held_frames
> -     *        ^ - lcl_offs_frames
> -     */
> -    advanced = stream->in_oss_frames - in_oss_frames;
> -    if(advanced > stream->held_frames)
> -        advanced = stream->held_frames;
> -    stream->lcl_offs_frames += advanced;
> -    stream->lcl_offs_frames %= stream->bufsize_frames;
> -    stream->held_frames -= advanced;
> -    stream->in_oss_frames = in_oss_frames;
> -    TRACE("advanced by %lu, lcl_offs: %u, held: %u, in_oss: %u\n",
> -            advanced, stream->lcl_offs_frames, stream->held_frames, stream->in_oss_frames);
> -
> -
> -    if(stream->held_frames == stream->in_oss_frames)
> -        return;
> -
> -    write_offs_frames = (stream->lcl_offs_frames + stream->in_oss_frames) % stream->bufsize_frames;
> -    new_frames = stream->held_frames - stream->in_oss_frames;
> -
> -    if(write_offs_frames + new_frames > stream->bufsize_frames)
> -        to_write_frames = stream->bufsize_frames - write_offs_frames;
> -    else
> -        to_write_frames = new_frames;
> -
> -    to_write_frames = min(to_write_frames, write_limit);
> -    to_write_bytes = to_write_frames * stream->fmt->nBlockAlign;
> -    TRACE("going to write %lu frames from %u (%lu of %u)\n", to_write_frames,
> -            write_offs_frames, to_write_frames + write_offs_frames,
> -            stream->bufsize_frames);
> -
> -    buf = stream->local_buffer + write_offs_frames * stream->fmt->nBlockAlign;
> -
> -    if(stream->mute)
> -        silence_buffer(stream, buf, to_write_frames);
> -
> -    written_bytes = write(stream->fd, buf, to_write_bytes);
> -    if(written_bytes < 0){
> -        /* EAGAIN is OSS buffer full, log that too */
> -        WARN("write failed: %d (%s)\n", errno, strerror(errno));
> -        return;
> -    }
> -    written_frames = written_bytes / stream->fmt->nBlockAlign;
> -
> -    stream->in_oss_frames += written_frames;
> -
> -    if(written_frames < to_write_frames){
> -        /* OSS buffer probably full */
> -        return;
> -    }
> -
> -    if(new_frames > written_frames && written_frames < write_limit){
> -        /* wrapped and have some data back at the start to write */
> -
> -        to_write_frames = min(write_limit - written_frames, new_frames - written_frames);
> -        to_write_bytes = to_write_frames * stream->fmt->nBlockAlign;
> -
> -        if(stream->mute)
> -            silence_buffer(stream, stream->local_buffer, to_write_frames);
> -
> -        TRACE("wrapping to write %lu frames from beginning\n", to_write_frames);
> -
> -        written_bytes = write(stream->fd, stream->local_buffer, to_write_bytes);
> -        if(written_bytes < 0){
> -            WARN("write failed: %d (%s)\n", errno, strerror(errno));
> -            return;
> -        }
> -        written_frames = written_bytes / stream->fmt->nBlockAlign;
> -        stream->in_oss_frames += written_frames;
> -    }
> -}
> -
> -static void oss_read_data(struct oss_stream *stream)
> -{
> -    UINT64 pos, readable;
> -    ssize_t nread;
> -
> -    pos = (stream->held_frames + stream->lcl_offs_frames) % stream->bufsize_frames;
> -    readable = (stream->bufsize_frames - pos) * stream->fmt->nBlockAlign;
> -
> -    nread = read(stream->fd, stream->local_buffer + pos * stream->fmt->nBlockAlign,
> -            readable);
> -    if(nread < 0){
> -        WARN("read failed: %d (%s)\n", errno, strerror(errno));
> -        return;
> -    }
> -
> -    stream->held_frames += nread / stream->fmt->nBlockAlign;
> -
> -    if(stream->held_frames > stream->bufsize_frames){
> -        WARN("Overflow of unread data\n");
> -        stream->lcl_offs_frames += stream->held_frames;
> -        stream->lcl_offs_frames %= stream->bufsize_frames;
> -        stream->held_frames = stream->bufsize_frames;
> -    }
> -}
> -
> -static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
> -{
> -    ACImpl *This = user;
> -    struct oss_stream *stream = This->stream;
> -
> -    oss_lock(stream);
> -
> -    if(stream->playing){
> -        if(stream->flow == eRender && stream->held_frames)
> -            oss_write_data(stream);
> -        else if(stream->flow == eCapture)
> -            oss_read_data(stream);
> -    }
> -
> -    if(stream->event)
> -        SetEvent(stream->event);
> -
> -    oss_unlock(stream);
> -}
> -
>  static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
>  {
>      ACImpl *This = impl_from_IAudioClient3(iface);
> @@ -1118,11 +959,9 @@ static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
>          return AUDCLNT_E_NOT_STOPPED;
>      }
>  
> -    if(!This->timer){
> -        if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
> -                    oss_period_callback, This, 0, stream->period / 10000,
> -                    WT_EXECUTEINTIMERTHREAD))
> -            ERR("Unable to create period timer: %u\n", GetLastError());
> +    if(!This->timer_thread){
> +        This->timer_thread = CreateThread(NULL, 0, timer_thread, This->stream, 0, NULL);
> +        SetThreadPriority(This->timer_thread, THREAD_PRIORITY_TIME_CRITICAL);
>      }
>  
>      stream->playing = TRUE;
> diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c
> index b00925d6e9b..b0dc22a500f 100644
> --- a/dlls/wineoss.drv/oss.c
> +++ b/dlls/wineoss.drv/oss.c
> @@ -604,6 +604,12 @@ static NTSTATUS release_stream(void *args)
>      struct oss_stream *stream = params->stream;
>      SIZE_T size;
>  
> +    if(params->timer_thread){
> +        stream->please_quit = TRUE;
> +        NtWaitForSingleObject(params->timer_thread, FALSE, NULL);
> +        NtClose(params->timer_thread);
> +    }
> +
>      close(stream->fd);
>      if(stream->local_buffer){
>          size = 0;
> @@ -621,6 +627,195 @@ static NTSTATUS release_stream(void *args)
>      return STATUS_SUCCESS;
>  }
>  
> +static void silence_buffer(struct oss_stream *stream, BYTE *buffer, UINT32 frames)
> +{
> +    WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)stream->fmt;
> +    if((stream->fmt->wFormatTag == WAVE_FORMAT_PCM ||
> +            (stream->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> +             IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
> +            stream->fmt->wBitsPerSample == 8)
> +        memset(buffer, 128, frames * stream->fmt->nBlockAlign);
> +    else
> +        memset(buffer, 0, frames * stream->fmt->nBlockAlign);
> +}
> +
> +static void oss_write_data(struct oss_stream *stream)
> +{
> +    ssize_t written_bytes;
> +    UINT32 written_frames, in_oss_frames, write_limit, max_period, write_offs_frames, new_frames;
> +    SIZE_T to_write_frames, to_write_bytes, advanced;
> +    audio_buf_info bi;
> +    BYTE *buf;
> +
> +    if(ioctl(stream->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
> +        WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
> +        return;
> +    }
> +
> +    max_period = max(bi.fragsize / stream->fmt->nBlockAlign, stream->period_frames);
> +
> +    if(bi.bytes > stream->oss_bufsize_bytes){
> +        TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
> +                bi.bytes, stream->oss_bufsize_bytes);
> +        stream->oss_bufsize_bytes = bi.bytes;
> +        in_oss_frames = 0;
> +    }else
> +        in_oss_frames = (stream->oss_bufsize_bytes - bi.bytes) / stream->fmt->nBlockAlign;
> +
> +    if(in_oss_frames > stream->in_oss_frames){
> +        TRACE("Capping reported frames from %u to %u\n",
> +                in_oss_frames, stream->in_oss_frames);
> +        in_oss_frames = stream->in_oss_frames;
> +    }
> +
> +    write_limit = 0;
> +    while(write_limit + in_oss_frames < max_period * 3)
> +        write_limit += max_period;
> +    if(write_limit == 0)
> +        return;
> +
> +    /*        vvvvvv - in_oss_frames
> +     * [--xxxxxxxxxx]
> +     *       [xxxxxxxxxx--]
> +     *        ^^^^^^^^^^ - held_frames
> +     *        ^ - lcl_offs_frames
> +     */
> +    advanced = stream->in_oss_frames - in_oss_frames;
> +    if(advanced > stream->held_frames)
> +        advanced = stream->held_frames;
> +    stream->lcl_offs_frames += advanced;
> +    stream->lcl_offs_frames %= stream->bufsize_frames;
> +    stream->held_frames -= advanced;
> +    stream->in_oss_frames = in_oss_frames;
> +    TRACE("advanced by %lu, lcl_offs: %u, held: %u, in_oss: %u\n",
> +            advanced, stream->lcl_offs_frames, stream->held_frames, stream->in_oss_frames);
> +
> +
> +    if(stream->held_frames == stream->in_oss_frames)
> +        return;
> +
> +    write_offs_frames = (stream->lcl_offs_frames + stream->in_oss_frames) % stream->bufsize_frames;
> +    new_frames = stream->held_frames - stream->in_oss_frames;
> +
> +    if(write_offs_frames + new_frames > stream->bufsize_frames)
> +        to_write_frames = stream->bufsize_frames - write_offs_frames;
> +    else
> +        to_write_frames = new_frames;
> +
> +    to_write_frames = min(to_write_frames, write_limit);
> +    to_write_bytes = to_write_frames * stream->fmt->nBlockAlign;
> +    TRACE("going to write %lu frames from %u (%lu of %u)\n", to_write_frames,
> +            write_offs_frames, to_write_frames + write_offs_frames,
> +            stream->bufsize_frames);
> +
> +    buf = stream->local_buffer + write_offs_frames * stream->fmt->nBlockAlign;
> +
> +    if(stream->mute)
> +        silence_buffer(stream, buf, to_write_frames);
> +
> +    written_bytes = write(stream->fd, buf, to_write_bytes);
> +    if(written_bytes < 0){
> +        /* EAGAIN is OSS buffer full, log that too */
> +        WARN("write failed: %d (%s)\n", errno, strerror(errno));
> +        return;
> +    }
> +    written_frames = written_bytes / stream->fmt->nBlockAlign;
> +
> +    stream->in_oss_frames += written_frames;
> +
> +    if(written_frames < to_write_frames){
> +        /* OSS buffer probably full */
> +        return;
> +    }
> +
> +    if(new_frames > written_frames && written_frames < write_limit){
> +        /* wrapped and have some data back at the start to write */
> +
> +        to_write_frames = min(write_limit - written_frames, new_frames - written_frames);
> +        to_write_bytes = to_write_frames * stream->fmt->nBlockAlign;
> +
> +        if(stream->mute)
> +            silence_buffer(stream, stream->local_buffer, to_write_frames);
> +
> +        TRACE("wrapping to write %lu frames from beginning\n", to_write_frames);
> +
> +        written_bytes = write(stream->fd, stream->local_buffer, to_write_bytes);
> +        if(written_bytes < 0){
> +            WARN("write failed: %d (%s)\n", errno, strerror(errno));
> +            return;
> +        }
> +        written_frames = written_bytes / stream->fmt->nBlockAlign;
> +        stream->in_oss_frames += written_frames;
> +    }
> +}
> +
> +static void oss_read_data(struct oss_stream *stream)
> +{
> +    UINT64 pos, readable;
> +    ssize_t nread;
> +
> +    pos = (stream->held_frames + stream->lcl_offs_frames) % stream->bufsize_frames;
> +    readable = (stream->bufsize_frames - pos) * stream->fmt->nBlockAlign;
> +
> +    nread = read(stream->fd, stream->local_buffer + pos * stream->fmt->nBlockAlign,
> +            readable);
> +    if(nread < 0){
> +        WARN("read failed: %d (%s)\n", errno, strerror(errno));
> +        return;
> +    }
> +
> +    stream->held_frames += nread / stream->fmt->nBlockAlign;
> +
> +    if(stream->held_frames > stream->bufsize_frames){
> +        WARN("Overflow of unread data\n");
> +        stream->lcl_offs_frames += stream->held_frames;
> +        stream->lcl_offs_frames %= stream->bufsize_frames;
> +        stream->held_frames = stream->bufsize_frames;
> +    }
> +}
> +
> +static NTSTATUS timer_loop(void *args)
> +{
> +    struct timer_loop_params *params = args;
> +    struct oss_stream *stream = params->stream;
> +    LARGE_INTEGER delay, now, next;
> +    int adjust;
> +
> +    oss_lock(stream);
> +
> +    delay.QuadPart = -stream->period;
> +    NtQueryPerformanceCounter(&now, NULL);
> +    next.QuadPart = now.QuadPart + stream->period;
> +
> +    while(!stream->please_quit){
> +        if(stream->playing){
> +            if(stream->flow == eRender && stream->held_frames)
> +                oss_write_data(stream);
> +            else if(stream->flow == eCapture)
> +                oss_read_data(stream);
> +        }
> +        if(stream->event)
> +            NtSetEvent(stream->event, NULL);
> +        oss_unlock(stream);
> +
> +        NtDelayExecution(FALSE, &delay);
> +
> +        oss_lock(stream);
> +        NtQueryPerformanceCounter(&now, NULL);
> +        adjust = next.QuadPart - now.QuadPart;
> +        if(adjust > stream->period / 2)
> +            adjust = stream->period / 2;
> +        else if(adjust < -stream->period / 2)
> +            adjust = -stream->period / 2;
> +        delay.QuadPart = -(stream->period + adjust);
> +        next.QuadPart += stream->period;
> +    }
> +
> +    oss_unlock(stream);
> +
> +    return STATUS_SUCCESS;
> +}
> +
>  static NTSTATUS is_format_supported(void *args)
>  {
>      struct is_format_supported_params *params = args;
> @@ -794,6 +989,7 @@ unixlib_entry_t __wine_unix_call_funcs[] =
>      get_endpoint_ids,
>      create_stream,
>      release_stream,
> +    timer_loop,
>      is_format_supported,
>      get_mix_format,
>      get_buffer_size,
> diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h
> index 64c954cc769..2902bceb949 100644
> --- a/dlls/wineoss.drv/unixlib.h
> +++ b/dlls/wineoss.drv/unixlib.h
> @@ -28,7 +28,7 @@ struct oss_stream
>  
>      int fd;
>  
> -    BOOL playing, mute;
> +    BOOL playing, mute, please_quit;
>      UINT64 written_frames, last_pos_frames;
>      UINT32 period_frames, bufsize_frames, held_frames, tmp_buffer_frames, in_oss_frames;
>      UINT32 oss_bufsize_bytes, lcl_offs_frames; /* offs into local_buffer where valid data starts */
> @@ -86,9 +86,15 @@ struct create_stream_params
>  struct release_stream_params
>  {
>      struct oss_stream *stream;
> +    HANDLE timer_thread;
>      HRESULT result;
>  };
>  
> +struct timer_loop_params
> +{
> +    struct oss_stream *stream;
> +};
> +
>  struct is_format_supported_params
>  {
>      const char *device;
> @@ -134,6 +140,7 @@ enum oss_funcs
>      oss_get_endpoint_ids,
>      oss_create_stream,
>      oss_release_stream,
> +    oss_timer_loop,
>      oss_is_format_supported,
>      oss_get_mix_format,
>      oss_get_buffer_size,
> -- 
> 2.25.1
> 
> 



More information about the wine-devel mailing list