From: Liam Murphy Subject: [PATCH v4 2/2] winmm: Don't do anything in `WINMM_BeginPlaying` if the device is already playing Message-Id: <20220125004148.33704-2-liampm32@gmail.com> Date: Tue, 25 Jan 2022 11:41:48 +1100 In-Reply-To: <20220125004148.33704-1-liampm32@gmail.com> References: <20220125004148.33704-1-liampm32@gmail.com> This stops wave output devices' callbacks from being called reentrantly when they call `waveOutWrite`. It was occuring because `waveOutWrite` calls `WINMM_BeginPlaying`, which calls `WOD_PushData`, which checks for completed audio frames and calls the callback to notify it if there are any. By only calling `WOD_PushData` if the device isn't already playing, this effectively eliminates the problem. The callback will only be called when a frame is completed, so for it to be called when the device is stopped, it must have been the completion of the last frame in the queue. However, that means that no more `WOM_DONE` events are occuring which could trigger the callback. If it's called on the completion of the second-last frame, `device->stopped` can't be set by calling `waveOutWrite`, since it's updated within `WOD_PushData`, which won't get called if the device isn't stopped. It is still possible to trigger the callback reentrantly by other means (namely, by using `waveOutRestart`), but the desired behaviour is only that it can't be called reentrantly through just `waveOutWrite`. Signed-off-by: Liam Murphy --- dlls/winmm/tests/wave.c | 1 - dlls/winmm/waveform.c | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dlls/winmm/tests/wave.c b/dlls/winmm/tests/wave.c index 83e83a0150e..261eee76109 100644 --- a/dlls/winmm/tests/wave.c +++ b/dlls/winmm/tests/wave.c @@ -881,7 +881,6 @@ static void CALLBACK reentrancy_callback_func(HWAVEOUT hwo, UINT uMsg, data->call_num += 1; - todo_wine_if(data->call_num == 3) ok(data->running_thread != GetCurrentThreadId(), "winmm callback called reentrantly, with message %u\n", uMsg); if (data->running_thread) { if (data->running_thread != GetCurrentThreadId()) trace("Callback running on two threads simultaneously\n"); diff --git a/dlls/winmm/waveform.c b/dlls/winmm/waveform.c index 1159b48b483..3a6fc75022b 100644 --- a/dlls/winmm/waveform.c +++ b/dlls/winmm/waveform.c @@ -1948,11 +1948,11 @@ static MMRESULT WINMM_BeginPlaying(WINMM_Device *device) TRACE("(%p)\n", device->handle); - if(device->render) - /* prebuffer data before starting */ - WOD_PushData(device); - if(device->stopped){ + if(device->render) + /* prebuffer data before starting */ + WOD_PushData(device); + device->stopped = FALSE; hr = IAudioClient_Start(device->client); -- 2.35.0