mfplat/sample: Optimize copying to 2d buffer.
[wine.git] / dlls / sapi / mmaudio.c
blob7452d9a7257f8207dd712d917978c95fbc4648fa
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 ISpObjectToken_AddRef(token);
297 This->token = token;
298 return S_OK;
301 static HRESULT WINAPI objwithtoken_GetObjectToken(ISpObjectWithToken *iface, ISpObjectToken **token)
303 struct mmaudio *This = impl_from_ISpObjectWithToken(iface);
305 TRACE("(%p, %p).\n", iface, token);
307 if (!token)
308 return E_POINTER;
310 *token = This->token;
311 if (*token)
313 ISpObjectToken_AddRef(*token);
314 return S_OK;
316 else
317 return S_FALSE;
320 static const ISpObjectWithTokenVtbl objwithtoken_vtbl =
322 objwithtoken_QueryInterface,
323 objwithtoken_AddRef,
324 objwithtoken_Release,
325 objwithtoken_SetObjectToken,
326 objwithtoken_GetObjectToken
329 static HRESULT WINAPI mmsysaudio_QueryInterface(ISpMMSysAudio *iface, REFIID iid, void **obj)
331 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
333 TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj);
335 if (IsEqualIID(iid, &IID_IUnknown) ||
336 IsEqualIID(iid, &IID_ISequentialStream) ||
337 IsEqualIID(iid, &IID_IStream) ||
338 IsEqualIID(iid, &IID_ISpStreamFormat) ||
339 IsEqualIID(iid, &IID_ISpAudio) ||
340 IsEqualIID(iid, &IID_ISpMMSysAudio))
341 *obj = &This->ISpMMSysAudio_iface;
342 else if (IsEqualIID(iid, &IID_ISpEventSource))
343 *obj = &This->ISpEventSource_iface;
344 else if (IsEqualIID(iid, &IID_ISpEventSink))
345 *obj = &This->ISpEventSink_iface;
346 else if (IsEqualIID(iid, &IID_ISpObjectWithToken))
347 *obj = &This->ISpObjectWithToken_iface;
348 else
350 *obj = NULL;
351 FIXME("interface %s not implemented.\n", debugstr_guid(iid));
352 return E_NOINTERFACE;
355 IUnknown_AddRef((IUnknown *)*obj);
356 return S_OK;
359 static ULONG WINAPI mmsysaudio_AddRef(ISpMMSysAudio *iface)
361 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
362 ULONG ref = InterlockedIncrement(&This->ref);
364 TRACE("(%p): ref=%lu\n", iface, ref);
366 return ref;
369 static ULONG WINAPI mmsysaudio_Release(ISpMMSysAudio *iface)
371 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
372 ULONG ref = InterlockedDecrement(&This->ref);
374 TRACE("(%p): ref=%lu\n", iface, ref);
376 if (!ref)
378 ISpMMSysAudio_SetState(iface, SPAS_CLOSED, 0);
380 async_wait_queue_empty(&This->queue, INFINITE);
381 async_cancel_queue(&This->queue);
383 if (This->token) ISpObjectToken_Release(This->token);
384 heap_free(This->wfx);
385 CloseHandle(This->event);
386 DeleteCriticalSection(&This->pending_cs);
387 DeleteCriticalSection(&This->cs);
389 heap_free(This);
392 return ref;
395 static HRESULT WINAPI mmsysaudio_Read(ISpMMSysAudio *iface, void *pv, ULONG cb, ULONG *cb_read)
397 FIXME("(%p, %p, %lu, %p): stub.\n", iface, pv, cb, cb_read);
399 return E_NOTIMPL;
402 static HRESULT WINAPI mmsysaudio_Write(ISpMMSysAudio *iface, const void *pv, ULONG cb, ULONG *cb_written)
404 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
405 HRESULT hr = S_OK;
406 WAVEHDR *buf;
408 TRACE("(%p, %p, %lu, %p).\n", iface, pv, cb, cb_written);
410 if (This->flow != FLOW_OUT)
411 return STG_E_ACCESSDENIED;
413 if (cb_written)
414 *cb_written = 0;
416 EnterCriticalSection(&This->cs);
418 if (This->state == SPAS_CLOSED || This->state == SPAS_STOP)
420 LeaveCriticalSection(&This->cs);
421 return SP_AUDIO_STOPPED;
424 if (!(buf = heap_alloc(sizeof(WAVEHDR) + cb)))
426 LeaveCriticalSection(&This->cs);
427 return E_OUTOFMEMORY;
429 memcpy((char *)(buf + 1), pv, cb);
430 buf->lpData = (char *)(buf + 1);
431 buf->dwBufferLength = cb;
432 buf->dwFlags = 0;
434 if (waveOutPrepareHeader(This->hwave.out, buf, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
436 LeaveCriticalSection(&This->cs);
437 heap_free(buf);
438 return E_FAIL;
441 waveOutWrite(This->hwave.out, buf, sizeof(WAVEHDR));
443 EnterCriticalSection(&This->pending_cs);
444 ++This->pending_buf_count;
445 TRACE("pending_buf_count = %Iu\n", This->pending_buf_count);
446 LeaveCriticalSection(&This->pending_cs);
448 ResetEvent(This->event);
450 LeaveCriticalSection(&This->cs);
452 if (cb_written)
453 *cb_written = cb;
455 return hr;
458 static HRESULT WINAPI mmsysaudio_Seek(ISpMMSysAudio *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *new_pos)
460 FIXME("(%p, %s, %lu, %p): stub.\n", iface, wine_dbgstr_longlong(move.QuadPart), origin, new_pos);
462 return E_NOTIMPL;
465 static HRESULT WINAPI mmsysaudio_SetSize(ISpMMSysAudio *iface, ULARGE_INTEGER new_size)
467 FIXME("(%p, %s): stub.\n", iface, wine_dbgstr_longlong(new_size.QuadPart));
469 return E_NOTIMPL;
472 static HRESULT WINAPI mmsysaudio_CopyTo(ISpMMSysAudio *iface, IStream *stream, ULARGE_INTEGER cb,
473 ULARGE_INTEGER *cb_read, ULARGE_INTEGER *cb_written)
475 FIXME("(%p, %p, %s, %p, %p): stub.\n", iface, stream, wine_dbgstr_longlong(cb.QuadPart), cb_read, cb_written);
477 return E_NOTIMPL;
480 static HRESULT WINAPI mmsysaudio_Commit(ISpMMSysAudio *iface, DWORD flags)
482 FIXME("(%p, %#lx): stub.\n", iface, flags);
484 return E_NOTIMPL;
487 static HRESULT WINAPI mmsysaudio_Revert(ISpMMSysAudio *iface)
489 FIXME("(%p).\n", iface);
491 return E_NOTIMPL;
494 static HRESULT WINAPI mmsysaudio_LockRegion(ISpMMSysAudio *iface, ULARGE_INTEGER offset, ULARGE_INTEGER cb,
495 DWORD lock_type)
497 FIXME("(%p, %s, %s, %#lx): stub.\n", iface, wine_dbgstr_longlong(offset.QuadPart),
498 wine_dbgstr_longlong(cb.QuadPart), lock_type);
500 return E_NOTIMPL;
503 static HRESULT WINAPI mmsysaudio_UnlockRegion(ISpMMSysAudio *iface, ULARGE_INTEGER offset, ULARGE_INTEGER cb,
504 DWORD lock_type)
506 FIXME("(%p, %s, %s, %#lx): stub.\n", iface, wine_dbgstr_longlong(offset.QuadPart),
507 wine_dbgstr_longlong(cb.QuadPart), lock_type);
509 return E_NOTIMPL;
512 static HRESULT WINAPI mmsysaudio_Stat(ISpMMSysAudio *iface, STATSTG *statstg, DWORD flags)
514 FIXME("(%p, %p, %#lx): stub.\n", iface, statstg, flags);
516 return E_NOTIMPL;
519 static HRESULT WINAPI mmsysaudio_Clone(ISpMMSysAudio *iface, IStream **stream)
521 FIXME("(%p, %p): stub.\n", iface, stream);
523 return E_NOTIMPL;
526 static HRESULT WINAPI mmsysaudio_GetFormat(ISpMMSysAudio *iface, GUID *format, WAVEFORMATEX **wfx)
528 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
530 TRACE("(%p, %p, %p).\n", iface, format, wfx);
532 if (!format || !wfx)
533 return E_POINTER;
535 EnterCriticalSection(&This->cs);
537 if (!(*wfx = CoTaskMemAlloc(sizeof(WAVEFORMATEX) + This->wfx->cbSize)))
539 LeaveCriticalSection(&This->cs);
540 return E_OUTOFMEMORY;
542 *format = SPDFID_WaveFormatEx;
543 memcpy(*wfx, This->wfx, sizeof(WAVEFORMATEX) + This->wfx->cbSize);
545 LeaveCriticalSection(&This->cs);
547 return S_OK;
550 struct free_buf_task
552 struct async_task task;
553 struct mmaudio *audio;
554 WAVEHDR *buf;
557 static void free_out_buf_proc(struct async_task *task)
559 struct free_buf_task *fbt = (struct free_buf_task *)task;
560 size_t buf_count;
562 TRACE("(%p).\n", task);
564 waveOutUnprepareHeader(fbt->audio->hwave.out, fbt->buf, sizeof(WAVEHDR));
565 heap_free(fbt->buf);
567 EnterCriticalSection(&fbt->audio->pending_cs);
568 buf_count = --fbt->audio->pending_buf_count;
569 LeaveCriticalSection(&fbt->audio->pending_cs);
570 if (!buf_count)
571 SetEvent(fbt->audio->event);
572 TRACE("pending_buf_count = %Iu.\n", buf_count);
575 static void CALLBACK wave_out_proc(HWAVEOUT hwo, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
577 struct mmaudio *This = (struct mmaudio *)instance;
578 struct free_buf_task *task;
580 TRACE("(%p, %#x, %08Ix, %08Ix, %08Ix).\n", hwo, msg, instance, param1, param2);
582 switch (msg)
584 case WOM_DONE:
585 if (!(task = heap_alloc(sizeof(*task))))
587 ERR("failed to allocate free_buf_task.\n");
588 break;
590 task->task.proc = free_out_buf_proc;
591 task->audio = This;
592 task->buf = (WAVEHDR *)param1;
593 async_queue_task(&This->queue, (struct async_task *)task);
594 break;
596 default:
597 break;
601 static HRESULT WINAPI mmsysaudio_SetState(ISpMMSysAudio *iface, SPAUDIOSTATE state, ULONGLONG reserved)
603 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
604 HRESULT hr = S_OK;
606 TRACE("(%p, %u, %s).\n", iface, state, wine_dbgstr_longlong(reserved));
608 if (state != SPAS_CLOSED && state != SPAS_RUN)
610 FIXME("state %#x not implemented.\n", state);
611 return E_NOTIMPL;
614 EnterCriticalSection(&This->cs);
616 if (This->state == state)
617 goto done;
619 if (This->state == SPAS_CLOSED)
621 if (FAILED(hr = async_start_queue(&This->queue)))
623 ERR("Failed to start async queue: %#lx.\n", hr);
624 goto done;
627 if (waveOutOpen(&This->hwave.out, This->device_id, This->wfx, (DWORD_PTR)wave_out_proc,
628 (DWORD_PTR)This, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
630 hr = SPERR_GENERIC_MMSYS_ERROR;
631 goto done;
635 if (state == SPAS_CLOSED && This->state != SPAS_CLOSED)
637 waveOutReset(This->hwave.out);
638 /* Wait until all buffers are freed. */
639 WaitForSingleObject(This->event, INFINITE);
641 if (waveOutClose(This->hwave.out) != MMSYSERR_NOERROR)
643 hr = SPERR_GENERIC_MMSYS_ERROR;
644 goto done;
648 This->state = state;
650 done:
651 LeaveCriticalSection(&This->cs);
652 return hr;
655 static HRESULT WINAPI mmsysaudio_SetFormat(ISpMMSysAudio *iface, const GUID *guid, const WAVEFORMATEX *wfx)
657 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
658 MMRESULT res;
659 WAVEFORMATEX *new_wfx;
661 TRACE("(%p, %s, %p).\n", iface, debugstr_guid(guid), wfx);
663 if (!guid || !wfx || !IsEqualGUID(guid, &SPDFID_WaveFormatEx))
664 return E_INVALIDARG;
666 EnterCriticalSection(&This->cs);
668 if (!memcmp(wfx, This->wfx, sizeof(*wfx)) && !memcmp(wfx + 1, This->wfx + 1, wfx->cbSize))
670 LeaveCriticalSection(&This->cs);
671 return S_OK;
674 if (This->state != SPAS_CLOSED)
676 LeaveCriticalSection(&This->cs);
677 return SPERR_DEVICE_BUSY;
680 /* Determine whether the device supports the requested format. */
681 res = waveOutOpen(NULL, This->device_id, wfx, 0, 0, WAVE_FORMAT_QUERY);
682 if (res != MMSYSERR_NOERROR)
684 LeaveCriticalSection(&This->cs);
685 return res == WAVERR_BADFORMAT ? SPERR_UNSUPPORTED_FORMAT : SPERR_GENERIC_MMSYS_ERROR;
688 if (!(new_wfx = heap_alloc(sizeof(*wfx) + wfx->cbSize)))
690 LeaveCriticalSection(&This->cs);
691 return E_OUTOFMEMORY;
693 memcpy(new_wfx, wfx, sizeof(*wfx) + wfx->cbSize);
694 heap_free(This->wfx);
695 This->wfx = new_wfx;
697 LeaveCriticalSection(&This->cs);
699 return S_OK;
702 static HRESULT WINAPI mmsysaudio_GetStatus(ISpMMSysAudio *iface, SPAUDIOSTATUS *status)
704 FIXME("(%p, %p): stub.\n", iface, status);
706 return E_NOTIMPL;
709 static HRESULT WINAPI mmsysaudio_SetBufferInfo(ISpMMSysAudio *iface, const SPAUDIOBUFFERINFO *info)
711 FIXME("(%p, %p): stub.\n", iface, info);
713 return E_NOTIMPL;
716 static HRESULT WINAPI mmsysaudio_GetBufferInfo(ISpMMSysAudio *iface, SPAUDIOBUFFERINFO *info)
718 FIXME("(%p, %p): stub.\n", iface, info);
720 return E_NOTIMPL;
723 static HRESULT WINAPI mmsysaudio_GetDefaultFormat(ISpMMSysAudio *iface, GUID *guid, WAVEFORMATEX **wfx)
725 FIXME("(%p, %p, %p): stub.\n", iface, guid, wfx);
727 return E_NOTIMPL;
730 static HANDLE WINAPI mmsysaudio_EventHandle(ISpMMSysAudio *iface)
732 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
734 TRACE("(%p).\n", iface);
736 return This->event;
739 static HRESULT WINAPI mmsysaudio_GetVolumeLevel(ISpMMSysAudio *iface, ULONG *level)
741 FIXME("(%p, %p): stub.\n", iface, level);
743 return E_NOTIMPL;
746 static HRESULT WINAPI mmsysaudio_SetVolumeLevel(ISpMMSysAudio *iface, ULONG level)
748 FIXME("(%p, %lu): stub.\n", iface, level);
750 return E_NOTIMPL;
753 static HRESULT WINAPI mmsysaudio_GetBufferNotifySize(ISpMMSysAudio *iface, ULONG *size)
755 FIXME("(%p, %p): stub.\n", iface, size);
757 return E_NOTIMPL;
760 static HRESULT WINAPI mmsysaudio_SetBufferNotifySize(ISpMMSysAudio *iface, ULONG size)
762 FIXME("(%p, %lu): stub.\n", iface, size);
764 return E_NOTIMPL;
767 static HRESULT WINAPI mmsysaudio_GetDeviceId(ISpMMSysAudio *iface, UINT *id)
769 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
771 TRACE("(%p, %p).\n", iface, id);
773 if (!id) return E_POINTER;
775 EnterCriticalSection(&This->cs);
776 *id = This->device_id;
777 LeaveCriticalSection(&This->cs);
779 return S_OK;
782 static HRESULT WINAPI mmsysaudio_SetDeviceId(ISpMMSysAudio *iface, UINT id)
784 struct mmaudio *This = impl_from_ISpMMSysAudio(iface);
786 TRACE("(%p, %u).\n", iface, id);
788 if (id != WAVE_MAPPER && id >= waveOutGetNumDevs())
789 return E_INVALIDARG;
791 EnterCriticalSection(&This->cs);
793 if (id == This->device_id)
795 LeaveCriticalSection(&This->cs);
796 return S_OK;
798 if (This->state != SPAS_CLOSED)
800 LeaveCriticalSection(&This->cs);
801 return SPERR_DEVICE_BUSY;
803 This->device_id = id;
805 LeaveCriticalSection(&This->cs);
807 return S_OK;
810 static HRESULT WINAPI mmsysaudio_GetMMHandle(ISpMMSysAudio *iface, void **handle)
812 FIXME("(%p, %p): stub.\n", iface, handle);
814 return E_NOTIMPL;
817 static HRESULT WINAPI mmsysaudio_GetLineId(ISpMMSysAudio *iface, UINT *id)
819 FIXME("(%p, %p): stub.\n", iface, id);
821 return E_NOTIMPL;
824 static HRESULT WINAPI mmsysaudio_SetLineId(ISpMMSysAudio *iface, UINT id)
826 FIXME("(%p, %u): stub.\n", iface, id);
828 return E_NOTIMPL;
831 static const ISpMMSysAudioVtbl mmsysaudio_vtbl =
833 mmsysaudio_QueryInterface,
834 mmsysaudio_AddRef,
835 mmsysaudio_Release,
836 mmsysaudio_Read,
837 mmsysaudio_Write,
838 mmsysaudio_Seek,
839 mmsysaudio_SetSize,
840 mmsysaudio_CopyTo,
841 mmsysaudio_Commit,
842 mmsysaudio_Revert,
843 mmsysaudio_LockRegion,
844 mmsysaudio_UnlockRegion,
845 mmsysaudio_Stat,
846 mmsysaudio_Clone,
847 mmsysaudio_GetFormat,
848 mmsysaudio_SetState,
849 mmsysaudio_SetFormat,
850 mmsysaudio_GetStatus,
851 mmsysaudio_SetBufferInfo,
852 mmsysaudio_GetBufferInfo,
853 mmsysaudio_GetDefaultFormat,
854 mmsysaudio_EventHandle,
855 mmsysaudio_GetVolumeLevel,
856 mmsysaudio_SetVolumeLevel,
857 mmsysaudio_GetBufferNotifySize,
858 mmsysaudio_SetBufferNotifySize,
859 mmsysaudio_GetDeviceId,
860 mmsysaudio_SetDeviceId,
861 mmsysaudio_GetMMHandle,
862 mmsysaudio_GetLineId,
863 mmsysaudio_SetLineId
866 static HRESULT mmaudio_create(IUnknown *outer, REFIID iid, void **obj, enum flow_type flow)
868 struct mmaudio *This;
869 HRESULT hr;
871 if (flow != FLOW_OUT)
873 FIXME("flow %d not implemented.\n", flow);
874 return E_NOTIMPL;
877 if (!(This = heap_alloc_zero(sizeof(*This))))
878 return E_OUTOFMEMORY;
879 This->ISpEventSource_iface.lpVtbl = &event_source_vtbl;
880 This->ISpEventSink_iface.lpVtbl = &event_sink_vtbl;
881 This->ISpObjectWithToken_iface.lpVtbl = &objwithtoken_vtbl;
882 This->ISpMMSysAudio_iface.lpVtbl = &mmsysaudio_vtbl;
883 This->ref = 1;
885 This->flow = flow;
886 This->token = NULL;
887 This->device_id = WAVE_MAPPER;
888 This->state = SPAS_CLOSED;
890 if (!(This->wfx = heap_alloc(sizeof(*This->wfx))))
892 heap_free(This);
893 return E_OUTOFMEMORY;
895 This->wfx->wFormatTag = WAVE_FORMAT_PCM;
896 This->wfx->nChannels = 1;
897 This->wfx->nSamplesPerSec = 22050;
898 This->wfx->nAvgBytesPerSec = 22050 * 2;
899 This->wfx->nBlockAlign = 2;
900 This->wfx->wBitsPerSample = 16;
901 This->wfx->cbSize = 0;
903 This->pending_buf_count = 0;
904 This->event = CreateEventW(NULL, TRUE, TRUE, NULL);
906 InitializeCriticalSection(&This->cs);
907 InitializeCriticalSection(&This->pending_cs);
909 hr = ISpMMSysAudio_QueryInterface(&This->ISpMMSysAudio_iface, iid, obj);
910 ISpMMSysAudio_Release(&This->ISpMMSysAudio_iface);
911 return hr;
914 HRESULT mmaudio_out_create(IUnknown *outer, REFIID iid, void **obj)
916 return mmaudio_create(outer, iid, obj, FLOW_OUT);