sapi: Free completed buffers asynchronously in SpMMAudio.
[wine.git] / dlls / sapi / mmaudio.c
blobadd670ff3dc3d49868bb014e64117ccd1a51efa1
1 /*
2 * Speech API (SAPI) winmm audio implementation.
4 * Copyright 2023 Shaun Ren for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "objbase.h"
29 #include "sapiddk.h"
30 #include "sperror.h"
32 #include "initguid.h"
34 #include "wine/debug.h"
36 #include "sapi_private.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(sapi);
40 DEFINE_GUID(SPDFID_Text, 0x7ceef9f9, 0x3d13, 0x11d2, 0x9e, 0xe7, 0x00, 0xc0, 0x4f, 0x79, 0x73, 0x96);
41 DEFINE_GUID(SPDFID_WaveFormatEx, 0xc31adbae, 0x527f, 0x4ff5, 0xa2, 0x30, 0xf6, 0x2b, 0xb6, 0x1f, 0xf7, 0x0c);
43 enum flow_type { FLOW_IN, FLOW_OUT };
45 struct mmaudio
47 ISpEventSource ISpEventSource_iface;
48 ISpEventSink ISpEventSink_iface;
49 ISpObjectWithToken ISpObjectWithToken_iface;
50 ISpMMSysAudio ISpMMSysAudio_iface;
51 LONG ref;
53 enum flow_type flow;
54 ISpObjectToken *token;
55 UINT device_id;
56 SPAUDIOSTATE state;
57 WAVEFORMATEX *wfx;
58 union
60 HWAVEIN in;
61 HWAVEOUT out;
62 } hwave;
63 HANDLE event;
64 struct async_queue queue;
65 CRITICAL_SECTION cs;
67 size_t pending_buf_count;
68 CRITICAL_SECTION pending_cs;
71 static inline struct mmaudio *impl_from_ISpEventSource(ISpEventSource *iface)
73 return CONTAINING_RECORD(iface, struct mmaudio, ISpEventSource_iface);
76 static inline struct mmaudio *impl_from_ISpEventSink(ISpEventSink *iface)
78 return CONTAINING_RECORD(iface, struct mmaudio, ISpEventSink_iface);
81 static inline struct mmaudio *impl_from_ISpObjectWithToken(ISpObjectWithToken *iface)
83 return CONTAINING_RECORD(iface, struct mmaudio, ISpObjectWithToken_iface);
86 static inline struct mmaudio *impl_from_ISpMMSysAudio(ISpMMSysAudio *iface)
88 return CONTAINING_RECORD(iface, struct mmaudio, ISpMMSysAudio_iface);
91 static HRESULT WINAPI event_source_QueryInterface(ISpEventSource *iface, REFIID iid, void **obj)
93 struct mmaudio *This = impl_from_ISpEventSource(iface);
95 TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj);
97 return ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj);
100 static ULONG WINAPI event_source_AddRef(ISpEventSource *iface)
102 struct mmaudio *This = impl_from_ISpEventSource(iface);
104 TRACE("(%p).\n", iface);
106 return ISpMMSysAudio_AddRef(&This->ISpMMSysAudio_iface);
109 static ULONG WINAPI event_source_Release(ISpEventSource *iface)
111 struct mmaudio *This = impl_from_ISpEventSource(iface);
113 TRACE("(%p).\n", iface);
115 return ISpMMSysAudio_Release(&This->ISpMMSysAudio_iface);
118 static HRESULT WINAPI event_source_SetNotifySink(ISpEventSource *iface, ISpNotifySink *sink)
120 FIXME("(%p, %p): stub.\n", iface, sink);
122 return E_NOTIMPL;
125 static HRESULT WINAPI event_source_SetNotifyWindowMessage(ISpEventSource *iface, HWND hwnd,
126 UINT msg, WPARAM wparam, LPARAM lparam)
128 FIXME("(%p, %p, %u, %Ix, %Ix): stub.\n", iface, hwnd, msg, wparam, lparam);
130 return E_NOTIMPL;
133 static HRESULT WINAPI event_source_SetNotifyCallbackFunction(ISpEventSource *iface, SPNOTIFYCALLBACK *callback,
134 WPARAM wparam, LPARAM lparam)
136 FIXME("(%p, %p, %Ix, %Ix): stub.\n", iface, callback, wparam, lparam);
138 return E_NOTIMPL;
141 static HRESULT WINAPI event_source_SetNotifyCallbackInterface(ISpEventSource *iface, ISpNotifyCallback *callback,
142 WPARAM wparam, LPARAM lparam)
144 FIXME("(%p, %p, %Ix, %Ix): stub.\n", iface, callback, wparam, lparam);
146 return E_NOTIMPL;
149 static HRESULT WINAPI event_source_SetNotifyWin32Event(ISpEventSource *iface)
151 FIXME("(%p): stub.\n", iface);
153 return E_NOTIMPL;
156 static HRESULT WINAPI event_source_WaitForNotifyEvent(ISpEventSource *iface, DWORD milliseconds)
158 FIXME("(%p, %ld): stub.\n", iface, milliseconds);
160 return E_NOTIMPL;
163 static HANDLE WINAPI event_source_GetNotifyEventHandle(ISpEventSource *iface)
165 FIXME("(%p): stub.\n", iface);
167 return NULL;
170 static HRESULT WINAPI event_source_SetInterest(ISpEventSource *iface, ULONGLONG event, ULONGLONG queued)
172 FIXME("(%p, %s, %s): stub.\n", iface, wine_dbgstr_longlong(event), wine_dbgstr_longlong(queued));
174 return E_NOTIMPL;
177 static HRESULT WINAPI event_source_GetEvents(ISpEventSource *iface, ULONG count, SPEVENT *array, ULONG *fetched)
179 FIXME("(%p, %lu, %p, %p): stub.\n", iface, count, array, fetched);
181 return E_NOTIMPL;
184 static HRESULT WINAPI event_source_GetInfo(ISpEventSource *iface, SPEVENTSOURCEINFO *info)
186 FIXME("(%p, %p): stub.\n", iface, info);
188 return E_NOTIMPL;
191 static const ISpEventSourceVtbl event_source_vtbl =
193 event_source_QueryInterface,
194 event_source_AddRef,
195 event_source_Release,
196 event_source_SetNotifySink,
197 event_source_SetNotifyWindowMessage,
198 event_source_SetNotifyCallbackFunction,
199 event_source_SetNotifyCallbackInterface,
200 event_source_SetNotifyWin32Event,
201 event_source_WaitForNotifyEvent,
202 event_source_GetNotifyEventHandle,
203 event_source_SetInterest,
204 event_source_GetEvents,
205 event_source_GetInfo
208 static HRESULT WINAPI event_sink_QueryInterface(ISpEventSink *iface, REFIID iid, void **obj)
210 struct mmaudio *This = impl_from_ISpEventSink(iface);
212 TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj);
214 return ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj);
217 static ULONG WINAPI event_sink_AddRef(ISpEventSink *iface)
219 struct mmaudio *This = impl_from_ISpEventSink(iface);
221 TRACE("(%p).\n", iface);
223 return ISpMMSysAudio_AddRef(&This->ISpMMSysAudio_iface);
226 static ULONG WINAPI event_sink_Release(ISpEventSink *iface)
228 struct mmaudio *This = impl_from_ISpEventSink(iface);
230 TRACE("(%p).\n", iface);
232 return ISpMMSysAudio_Release(&This->ISpMMSysAudio_iface);
235 static HRESULT WINAPI event_sink_AddEvents(ISpEventSink *iface, const SPEVENT *events, ULONG count)
237 FIXME("(%p, %p, %lu).\n", iface, events, count);
239 return E_NOTIMPL;
242 static HRESULT WINAPI event_sink_GetEventInterest(ISpEventSink *iface, ULONGLONG *interest)
244 FIXME("(%p, %p).\n", iface, interest);
246 return E_NOTIMPL;
249 static const ISpEventSinkVtbl event_sink_vtbl =
251 event_sink_QueryInterface,
252 event_sink_AddRef,
253 event_sink_Release,
254 event_sink_AddEvents,
255 event_sink_GetEventInterest
258 static HRESULT WINAPI objwithtoken_QueryInterface(ISpObjectWithToken *iface, REFIID iid, void **obj)
260 struct mmaudio *This = impl_from_ISpObjectWithToken(iface);
262 TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj);
264 return ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj);
267 static ULONG WINAPI objwithtoken_AddRef(ISpObjectWithToken *iface)
269 struct mmaudio *This = impl_from_ISpObjectWithToken(iface);
271 TRACE("(%p).\n", iface);
273 return ISpMMSysAudio_AddRef(&This->ISpMMSysAudio_iface);
276 static ULONG WINAPI objwithtoken_Release(ISpObjectWithToken *iface)
278 struct mmaudio *This = impl_from_ISpObjectWithToken(iface);
280 TRACE("(%p).\n", iface);
282 return ISpMMSysAudio_Release(&This->ISpMMSysAudio_iface);
285 static HRESULT WINAPI objwithtoken_SetObjectToken(ISpObjectWithToken *iface, ISpObjectToken *token)
287 struct mmaudio *This = impl_from_ISpObjectWithToken(iface);
289 FIXME("(%p, %p): semi-stub.\n", iface, token);
291 if (!token)
292 return E_INVALIDARG;
293 if (This->token)
294 return SPERR_ALREADY_INITIALIZED;
296 This->token = token;
297 return S_OK;
300 static HRESULT WINAPI objwithtoken_GetObjectToken(ISpObjectWithToken *iface, ISpObjectToken **token)
302 struct mmaudio *This = impl_from_ISpObjectWithToken(iface);
304 TRACE("(%p, %p).\n", iface, token);
306 if (!token)
307 return E_POINTER;
309 *token = This->token;
310 if (*token)
312 ISpObjectToken_AddRef(*token);
313 return S_OK;
315 else
316 return S_FALSE;
319 static const ISpObjectWithTokenVtbl objwithtoken_vtbl =
321 objwithtoken_QueryInterface,
322 objwithtoken_AddRef,
323 objwithtoken_Release,
324 objwithtoken_SetObjectToken,
325 objwithtoken_GetObjectToken
328 static HRESULT WINAPI mmsysaudio_QueryInterface(ISpMMSysAudio *iface, REFIID iid, void **obj)
330 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
332 TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj);
334 if (IsEqualIID(iid, &IID_IUnknown) ||
335 IsEqualIID(iid, &IID_ISequentialStream) ||
336 IsEqualIID(iid, &IID_IStream) ||
337 IsEqualIID(iid, &IID_ISpStreamFormat) ||
338 IsEqualIID(iid, &IID_ISpAudio) ||
339 IsEqualIID(iid, &IID_ISpMMSysAudio))
340 *obj = &This->ISpMMSysAudio_iface;
341 else if (IsEqualIID(iid, &IID_ISpEventSource))
342 *obj = &This->ISpEventSource_iface;
343 else if (IsEqualIID(iid, &IID_ISpEventSink))
344 *obj = &This->ISpEventSink_iface;
345 else if (IsEqualIID(iid, &IID_ISpObjectWithToken))
346 *obj = &This->ISpObjectWithToken_iface;
347 else
349 *obj = NULL;
350 FIXME("interface %s not implemented.\n", debugstr_guid(iid));
351 return E_NOINTERFACE;
354 IUnknown_AddRef((IUnknown *)*obj);
355 return S_OK;
358 static ULONG WINAPI mmsysaudio_AddRef(ISpMMSysAudio *iface)
360 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
361 ULONG ref = InterlockedIncrement(&This->ref);
363 TRACE("(%p): ref=%lu\n", iface, ref);
365 return ref;
368 static ULONG WINAPI mmsysaudio_Release(ISpMMSysAudio *iface)
370 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
371 ULONG ref = InterlockedDecrement(&This->ref);
373 TRACE("(%p): ref=%lu\n", iface, ref);
375 if (!ref)
377 ISpMMSysAudio_SetState(iface, SPAS_CLOSED, 0);
379 async_wait_queue_empty(&This->queue, INFINITE);
380 async_cancel_queue(&This->queue);
382 if (This->token) ISpObjectToken_Release(This->token);
383 heap_free(This->wfx);
384 CloseHandle(This->event);
385 DeleteCriticalSection(&This->pending_cs);
386 DeleteCriticalSection(&This->cs);
388 heap_free(This);
391 return ref;
394 static HRESULT WINAPI mmsysaudio_Read(ISpMMSysAudio *iface, void *pv, ULONG cb, ULONG *cb_read)
396 FIXME("(%p, %p, %lu, %p): stub.\n", iface, pv, cb, cb_read);
398 return E_NOTIMPL;
401 static HRESULT WINAPI mmsysaudio_Write(ISpMMSysAudio *iface, const void *pv, ULONG cb, ULONG *cb_written)
403 FIXME("(%p, %p, %lu, %p): stub.\n", iface, pv, cb, cb_written);
405 return E_NOTIMPL;
408 static HRESULT WINAPI mmsysaudio_Seek(ISpMMSysAudio *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *new_pos)
410 FIXME("(%p, %s, %lu, %p): stub.\n", iface, wine_dbgstr_longlong(move.QuadPart), origin, new_pos);
412 return E_NOTIMPL;
415 static HRESULT WINAPI mmsysaudio_SetSize(ISpMMSysAudio *iface, ULARGE_INTEGER new_size)
417 FIXME("(%p, %s): stub.\n", iface, wine_dbgstr_longlong(new_size.QuadPart));
419 return E_NOTIMPL;
422 static HRESULT WINAPI mmsysaudio_CopyTo(ISpMMSysAudio *iface, IStream *stream, ULARGE_INTEGER cb,
423 ULARGE_INTEGER *cb_read, ULARGE_INTEGER *cb_written)
425 FIXME("(%p, %p, %s, %p, %p): stub.\n", iface, stream, wine_dbgstr_longlong(cb.QuadPart), cb_read, cb_written);
427 return E_NOTIMPL;
430 static HRESULT WINAPI mmsysaudio_Commit(ISpMMSysAudio *iface, DWORD flags)
432 FIXME("(%p, %#lx): stub.\n", iface, flags);
434 return E_NOTIMPL;
437 static HRESULT WINAPI mmsysaudio_Revert(ISpMMSysAudio *iface)
439 FIXME("(%p).\n", iface);
441 return E_NOTIMPL;
444 static HRESULT WINAPI mmsysaudio_LockRegion(ISpMMSysAudio *iface, ULARGE_INTEGER offset, ULARGE_INTEGER cb,
445 DWORD lock_type)
447 FIXME("(%p, %s, %s, %#lx): stub.\n", iface, wine_dbgstr_longlong(offset.QuadPart),
448 wine_dbgstr_longlong(cb.QuadPart), lock_type);
450 return E_NOTIMPL;
453 static HRESULT WINAPI mmsysaudio_UnlockRegion(ISpMMSysAudio *iface, ULARGE_INTEGER offset, ULARGE_INTEGER cb,
454 DWORD lock_type)
456 FIXME("(%p, %s, %s, %#lx): stub.\n", iface, wine_dbgstr_longlong(offset.QuadPart),
457 wine_dbgstr_longlong(cb.QuadPart), lock_type);
459 return E_NOTIMPL;
462 static HRESULT WINAPI mmsysaudio_Stat(ISpMMSysAudio *iface, STATSTG *statstg, DWORD flags)
464 FIXME("(%p, %p, %#lx): stub.\n", iface, statstg, flags);
466 return E_NOTIMPL;
469 static HRESULT WINAPI mmsysaudio_Clone(ISpMMSysAudio *iface, IStream **stream)
471 FIXME("(%p, %p): stub.\n", iface, stream);
473 return E_NOTIMPL;
476 static HRESULT WINAPI mmsysaudio_GetFormat(ISpMMSysAudio *iface, GUID *format, WAVEFORMATEX **wfx)
478 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
480 TRACE("(%p, %p, %p).\n", iface, format, wfx);
482 if (!format || !wfx)
483 return E_POINTER;
485 EnterCriticalSection(&This->cs);
487 if (!(*wfx = CoTaskMemAlloc(sizeof(WAVEFORMATEX) + This->wfx->cbSize)))
489 LeaveCriticalSection(&This->cs);
490 return E_OUTOFMEMORY;
492 *format = SPDFID_WaveFormatEx;
493 memcpy(*wfx, This->wfx, sizeof(WAVEFORMATEX) + This->wfx->cbSize);
495 LeaveCriticalSection(&This->cs);
497 return S_OK;
500 struct free_buf_task
502 struct async_task task;
503 struct mmaudio *audio;
504 WAVEHDR *buf;
507 static void free_out_buf_proc(struct async_task *task)
509 struct free_buf_task *fbt = (struct free_buf_task *)task;
510 size_t buf_count;
512 TRACE("(%p).\n", task);
514 waveOutUnprepareHeader(fbt->audio->hwave.out, fbt->buf, sizeof(WAVEHDR));
515 heap_free(fbt->buf);
517 EnterCriticalSection(&fbt->audio->pending_cs);
518 buf_count = --fbt->audio->pending_buf_count;
519 LeaveCriticalSection(&fbt->audio->pending_cs);
520 if (!buf_count)
521 SetEvent(fbt->audio->event);
522 TRACE("pending_buf_count = %Iu.\n", buf_count);
525 static void CALLBACK wave_out_proc(HWAVEOUT hwo, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
527 struct mmaudio *This = (struct mmaudio *)instance;
528 struct free_buf_task *task;
530 TRACE("(%p, %#x, %08Ix, %08Ix, %08Ix).\n", hwo, msg, instance, param1, param2);
532 switch (msg)
534 case WOM_DONE:
535 if (!(task = heap_alloc(sizeof(*task))))
537 ERR("failed to allocate free_buf_task.\n");
538 break;
540 task->task.proc = free_out_buf_proc;
541 task->audio = This;
542 task->buf = (WAVEHDR *)param1;
543 async_queue_task(&This->queue, (struct async_task *)task);
544 break;
546 default:
547 break;
551 static HRESULT WINAPI mmsysaudio_SetState(ISpMMSysAudio *iface, SPAUDIOSTATE state, ULONGLONG reserved)
553 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
554 HRESULT hr = S_OK;
556 TRACE("(%p, %u, %s).\n", iface, state, wine_dbgstr_longlong(reserved));
558 if (state != SPAS_CLOSED && state != SPAS_RUN)
560 FIXME("state %#x not implemented.\n", state);
561 return E_NOTIMPL;
564 EnterCriticalSection(&This->cs);
566 if (This->state == state)
567 goto done;
569 if (This->state == SPAS_CLOSED)
571 if (FAILED(hr = async_start_queue(&This->queue)))
573 ERR("Failed to start async queue: %#lx.\n", hr);
574 goto done;
577 if (waveOutOpen(&This->hwave.out, This->device_id, This->wfx, (DWORD_PTR)wave_out_proc,
578 (DWORD_PTR)This, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
580 hr = SPERR_GENERIC_MMSYS_ERROR;
581 goto done;
585 if (state == SPAS_CLOSED && This->state != SPAS_CLOSED)
587 waveOutReset(This->hwave.out);
588 /* Wait until all buffers are freed. */
589 WaitForSingleObject(This->event, INFINITE);
591 if (waveOutClose(This->hwave.out) != MMSYSERR_NOERROR)
593 hr = SPERR_GENERIC_MMSYS_ERROR;
594 goto done;
598 This->state = state;
600 done:
601 LeaveCriticalSection(&This->cs);
602 return hr;
605 static HRESULT WINAPI mmsysaudio_SetFormat(ISpMMSysAudio *iface, const GUID *guid, const WAVEFORMATEX *wfx)
607 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
608 MMRESULT res;
609 WAVEFORMATEX *new_wfx;
611 TRACE("(%p, %s, %p).\n", iface, debugstr_guid(guid), wfx);
613 if (!guid || !wfx || !IsEqualGUID(guid, &SPDFID_WaveFormatEx))
614 return E_INVALIDARG;
616 EnterCriticalSection(&This->cs);
618 if (!memcmp(wfx, This->wfx, sizeof(*wfx)) && !memcmp(wfx + 1, This->wfx + 1, wfx->cbSize))
620 LeaveCriticalSection(&This->cs);
621 return S_OK;
624 if (This->state != SPAS_CLOSED)
626 LeaveCriticalSection(&This->cs);
627 return SPERR_DEVICE_BUSY;
630 /* Determine whether the device supports the requested format. */
631 res = waveOutOpen(NULL, This->device_id, wfx, 0, 0, WAVE_FORMAT_QUERY);
632 if (res != MMSYSERR_NOERROR)
634 LeaveCriticalSection(&This->cs);
635 return res == WAVERR_BADFORMAT ? SPERR_UNSUPPORTED_FORMAT : SPERR_GENERIC_MMSYS_ERROR;
638 if (!(new_wfx = heap_alloc(sizeof(*wfx) + wfx->cbSize)))
640 LeaveCriticalSection(&This->cs);
641 return E_OUTOFMEMORY;
643 memcpy(new_wfx, wfx, sizeof(*wfx) + wfx->cbSize);
644 heap_free(This->wfx);
645 This->wfx = new_wfx;
647 LeaveCriticalSection(&This->cs);
649 return S_OK;
652 static HRESULT WINAPI mmsysaudio_GetStatus(ISpMMSysAudio *iface, SPAUDIOSTATUS *status)
654 FIXME("(%p, %p): stub.\n", iface, status);
656 return E_NOTIMPL;
659 static HRESULT WINAPI mmsysaudio_SetBufferInfo(ISpMMSysAudio *iface, const SPAUDIOBUFFERINFO *info)
661 FIXME("(%p, %p): stub.\n", iface, info);
663 return E_NOTIMPL;
666 static HRESULT WINAPI mmsysaudio_GetBufferInfo(ISpMMSysAudio *iface, SPAUDIOBUFFERINFO *info)
668 FIXME("(%p, %p): stub.\n", iface, info);
670 return E_NOTIMPL;
673 static HRESULT WINAPI mmsysaudio_GetDefaultFormat(ISpMMSysAudio *iface, GUID *guid, WAVEFORMATEX **wfx)
675 FIXME("(%p, %p, %p): stub.\n", iface, guid, wfx);
677 return E_NOTIMPL;
680 static HANDLE WINAPI mmsysaudio_EventHandle(ISpMMSysAudio *iface)
682 FIXME("(%p): stub.\n", iface);
684 return NULL;
687 static HRESULT WINAPI mmsysaudio_GetVolumeLevel(ISpMMSysAudio *iface, ULONG *level)
689 FIXME("(%p, %p): stub.\n", iface, level);
691 return E_NOTIMPL;
694 static HRESULT WINAPI mmsysaudio_SetVolumeLevel(ISpMMSysAudio *iface, ULONG level)
696 FIXME("(%p, %lu): stub.\n", iface, level);
698 return E_NOTIMPL;
701 static HRESULT WINAPI mmsysaudio_GetBufferNotifySize(ISpMMSysAudio *iface, ULONG *size)
703 FIXME("(%p, %p): stub.\n", iface, size);
705 return E_NOTIMPL;
708 static HRESULT WINAPI mmsysaudio_SetBufferNotifySize(ISpMMSysAudio *iface, ULONG size)
710 FIXME("(%p, %lu): stub.\n", iface, size);
712 return E_NOTIMPL;
715 static HRESULT WINAPI mmsysaudio_GetDeviceId(ISpMMSysAudio *iface, UINT *id)
717 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
719 TRACE("(%p, %p).\n", iface, id);
721 if (!id) return E_POINTER;
723 EnterCriticalSection(&This->cs);
724 *id = This->device_id;
725 LeaveCriticalSection(&This->cs);
727 return S_OK;
730 static HRESULT WINAPI mmsysaudio_SetDeviceId(ISpMMSysAudio *iface, UINT id)
732 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
734 TRACE("(%p, %u).\n", iface, id);
736 if (id != WAVE_MAPPER && id >= waveOutGetNumDevs())
737 return E_INVALIDARG;
739 EnterCriticalSection(&This->cs);
741 if (id == This->device_id)
743 LeaveCriticalSection(&This->cs);
744 return S_OK;
746 if (This->state != SPAS_CLOSED)
748 LeaveCriticalSection(&This->cs);
749 return SPERR_DEVICE_BUSY;
751 This->device_id = id;
753 LeaveCriticalSection(&This->cs);
755 return S_OK;
758 static HRESULT WINAPI mmsysaudio_GetMMHandle(ISpMMSysAudio *iface, void **handle)
760 FIXME("(%p, %p): stub.\n", iface, handle);
762 return E_NOTIMPL;
765 static HRESULT WINAPI mmsysaudio_GetLineId(ISpMMSysAudio *iface, UINT *id)
767 FIXME("(%p, %p): stub.\n", iface, id);
769 return E_NOTIMPL;
772 static HRESULT WINAPI mmsysaudio_SetLineId(ISpMMSysAudio *iface, UINT id)
774 FIXME("(%p, %u): stub.\n", iface, id);
776 return E_NOTIMPL;
779 static const ISpMMSysAudioVtbl mmsysaudio_vtbl =
781 mmsysaudio_QueryInterface,
782 mmsysaudio_AddRef,
783 mmsysaudio_Release,
784 mmsysaudio_Read,
785 mmsysaudio_Write,
786 mmsysaudio_Seek,
787 mmsysaudio_SetSize,
788 mmsysaudio_CopyTo,
789 mmsysaudio_Commit,
790 mmsysaudio_Revert,
791 mmsysaudio_LockRegion,
792 mmsysaudio_UnlockRegion,
793 mmsysaudio_Stat,
794 mmsysaudio_Clone,
795 mmsysaudio_GetFormat,
796 mmsysaudio_SetState,
797 mmsysaudio_SetFormat,
798 mmsysaudio_GetStatus,
799 mmsysaudio_SetBufferInfo,
800 mmsysaudio_GetBufferInfo,
801 mmsysaudio_GetDefaultFormat,
802 mmsysaudio_EventHandle,
803 mmsysaudio_GetVolumeLevel,
804 mmsysaudio_SetVolumeLevel,
805 mmsysaudio_GetBufferNotifySize,
806 mmsysaudio_SetBufferNotifySize,
807 mmsysaudio_GetDeviceId,
808 mmsysaudio_SetDeviceId,
809 mmsysaudio_GetMMHandle,
810 mmsysaudio_GetLineId,
811 mmsysaudio_SetLineId
814 static HRESULT mmaudio_create(IUnknown *outer, REFIID iid, void **obj, enum flow_type flow)
816 struct mmaudio *This;
817 HRESULT hr;
819 if (flow != FLOW_OUT)
821 FIXME("flow %d not implemented.\n", flow);
822 return E_NOTIMPL;
825 if (!(This = heap_alloc_zero(sizeof(*This))))
826 return E_OUTOFMEMORY;
827 This->ISpEventSource_iface.lpVtbl = &event_source_vtbl;
828 This->ISpEventSink_iface.lpVtbl = &event_sink_vtbl;
829 This->ISpObjectWithToken_iface.lpVtbl = &objwithtoken_vtbl;
830 This->ISpMMSysAudio_iface.lpVtbl = &mmsysaudio_vtbl;
831 This->ref = 1;
833 This->flow = flow;
834 This->token = NULL;
835 This->device_id = WAVE_MAPPER;
836 This->state = SPAS_CLOSED;
838 if (!(This->wfx = heap_alloc(sizeof(*This->wfx))))
840 heap_free(This);
841 return E_OUTOFMEMORY;
843 This->wfx->wFormatTag = WAVE_FORMAT_PCM;
844 This->wfx->nChannels = 1;
845 This->wfx->nSamplesPerSec = 22050;
846 This->wfx->nAvgBytesPerSec = 22050 * 2;
847 This->wfx->nBlockAlign = 2;
848 This->wfx->wBitsPerSample = 16;
849 This->wfx->cbSize = 0;
851 This->pending_buf_count = 0;
852 This->event = CreateEventW(NULL, TRUE, TRUE, NULL);
854 InitializeCriticalSection(&This->cs);
855 InitializeCriticalSection(&This->pending_cs);
857 hr = ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj);
858 ISpMMSysAudio_Release(&This->ISpMMSysAudio_iface);
859 return hr;
862 HRESULT mmaudio_out_create(IUnknown *outer, REFIID iid, void **obj)
864 return mmaudio_create(outer, iid, obj, FLOW_OUT);