Signed-off-by: Huw Davies huw@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, ¶ms);
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, ¶ms); + + 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,
Signed-off-by: Andrew Eikum aeikum@codeweavers.com
On Wed, Apr 13, 2022 at 07:38:48AM +0100, Huw Davies wrote:
Signed-off-by: Huw Davies huw@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, ¶ms);
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, ¶ms);
- 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);
} return ref;stream_release(This->stream, This->timer_thread); HeapFree(GetProcessHeap(), 0, This);
@@ -765,7 +761,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
exit: if(FAILED(params.result)){
stream_release(stream);
} else {stream_release(stream, NULL); HeapFree(GetProcessHeap(), 0, This->vols); This->vols = NULL;
@@ -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