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
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
};
47 ISpEventSource ISpEventSource_iface
;
48 ISpEventSink ISpEventSink_iface
;
49 ISpObjectWithToken ISpObjectWithToken_iface
;
50 ISpMMSysAudio ISpMMSysAudio_iface
;
54 ISpObjectToken
*token
;
64 struct async_queue queue
;
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
);
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
);
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
);
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
);
149 static HRESULT WINAPI
event_source_SetNotifyWin32Event(ISpEventSource
*iface
)
151 FIXME("(%p): stub.\n", iface
);
156 static HRESULT WINAPI
event_source_WaitForNotifyEvent(ISpEventSource
*iface
, DWORD milliseconds
)
158 FIXME("(%p, %ld): stub.\n", iface
, milliseconds
);
163 static HANDLE WINAPI
event_source_GetNotifyEventHandle(ISpEventSource
*iface
)
165 FIXME("(%p): stub.\n", iface
);
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
));
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
);
184 static HRESULT WINAPI
event_source_GetInfo(ISpEventSource
*iface
, SPEVENTSOURCEINFO
*info
)
186 FIXME("(%p, %p): stub.\n", iface
, info
);
191 static const ISpEventSourceVtbl event_source_vtbl
=
193 event_source_QueryInterface
,
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
,
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
);
242 static HRESULT WINAPI
event_sink_GetEventInterest(ISpEventSink
*iface
, ULONGLONG
*interest
)
244 FIXME("(%p, %p).\n", iface
, interest
);
249 static const ISpEventSinkVtbl event_sink_vtbl
=
251 event_sink_QueryInterface
,
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
);
294 return SPERR_ALREADY_INITIALIZED
;
296 ISpObjectToken_AddRef(token
);
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
);
310 *token
= This
->token
;
313 ISpObjectToken_AddRef(*token
);
320 static const ISpObjectWithTokenVtbl objwithtoken_vtbl
=
322 objwithtoken_QueryInterface
,
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
;
351 FIXME("interface %s not implemented.\n", debugstr_guid(iid
));
352 return E_NOINTERFACE
;
355 IUnknown_AddRef((IUnknown
*)*obj
);
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
);
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
);
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
);
385 CloseHandle(This
->event
);
386 DeleteCriticalSection(&This
->pending_cs
);
387 DeleteCriticalSection(&This
->cs
);
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
);
402 static HRESULT WINAPI
mmsysaudio_Write(ISpMMSysAudio
*iface
, const void *pv
, ULONG cb
, ULONG
*cb_written
)
404 struct mmaudio
*This
= impl_from_ISpMMSysAudio(iface
);
408 TRACE("(%p, %p, %lu, %p).\n", iface
, pv
, cb
, cb_written
);
410 if (This
->flow
!= FLOW_OUT
)
411 return STG_E_ACCESSDENIED
;
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
= malloc(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
;
434 if (waveOutPrepareHeader(This
->hwave
.out
, buf
, sizeof(WAVEHDR
)) != MMSYSERR_NOERROR
)
436 LeaveCriticalSection(&This
->cs
);
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
);
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
);
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
));
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
);
480 static HRESULT WINAPI
mmsysaudio_Commit(ISpMMSysAudio
*iface
, DWORD flags
)
482 FIXME("(%p, %#lx): stub.\n", iface
, flags
);
487 static HRESULT WINAPI
mmsysaudio_Revert(ISpMMSysAudio
*iface
)
489 FIXME("(%p).\n", iface
);
494 static HRESULT WINAPI
mmsysaudio_LockRegion(ISpMMSysAudio
*iface
, ULARGE_INTEGER offset
, ULARGE_INTEGER cb
,
497 FIXME("(%p, %s, %s, %#lx): stub.\n", iface
, wine_dbgstr_longlong(offset
.QuadPart
),
498 wine_dbgstr_longlong(cb
.QuadPart
), lock_type
);
503 static HRESULT WINAPI
mmsysaudio_UnlockRegion(ISpMMSysAudio
*iface
, ULARGE_INTEGER offset
, ULARGE_INTEGER cb
,
506 FIXME("(%p, %s, %s, %#lx): stub.\n", iface
, wine_dbgstr_longlong(offset
.QuadPart
),
507 wine_dbgstr_longlong(cb
.QuadPart
), lock_type
);
512 static HRESULT WINAPI
mmsysaudio_Stat(ISpMMSysAudio
*iface
, STATSTG
*statstg
, DWORD flags
)
514 FIXME("(%p, %p, %#lx): stub.\n", iface
, statstg
, flags
);
519 static HRESULT WINAPI
mmsysaudio_Clone(ISpMMSysAudio
*iface
, IStream
**stream
)
521 FIXME("(%p, %p): stub.\n", iface
, stream
);
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
);
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
);
552 struct async_task task
;
553 struct mmaudio
*audio
;
557 static void free_out_buf_proc(struct async_task
*task
)
559 struct free_buf_task
*fbt
= (struct free_buf_task
*)task
;
562 TRACE("(%p).\n", task
);
564 waveOutUnprepareHeader(fbt
->audio
->hwave
.out
, fbt
->buf
, sizeof(WAVEHDR
));
567 EnterCriticalSection(&fbt
->audio
->pending_cs
);
568 buf_count
= --fbt
->audio
->pending_buf_count
;
569 LeaveCriticalSection(&fbt
->audio
->pending_cs
);
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
);
585 if (!(task
= malloc(sizeof(*task
))))
587 ERR("failed to allocate free_buf_task.\n");
590 task
->task
.proc
= free_out_buf_proc
;
592 task
->buf
= (WAVEHDR
*)param1
;
593 async_queue_task(&This
->queue
, (struct async_task
*)task
);
601 static HRESULT WINAPI
mmsysaudio_SetState(ISpMMSysAudio
*iface
, SPAUDIOSTATE state
, ULONGLONG reserved
)
603 struct mmaudio
*This
= impl_from_ISpMMSysAudio(iface
);
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
);
614 EnterCriticalSection(&This
->cs
);
616 if (This
->state
== state
)
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
);
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
;
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
;
651 LeaveCriticalSection(&This
->cs
);
655 static HRESULT WINAPI
mmsysaudio_SetFormat(ISpMMSysAudio
*iface
, const GUID
*guid
, const WAVEFORMATEX
*wfx
)
657 struct mmaudio
*This
= impl_from_ISpMMSysAudio(iface
);
659 WAVEFORMATEX
*new_wfx
;
661 TRACE("(%p, %s, %p).\n", iface
, debugstr_guid(guid
), wfx
);
663 if (!guid
|| !wfx
|| !IsEqualGUID(guid
, &SPDFID_WaveFormatEx
))
666 EnterCriticalSection(&This
->cs
);
668 if (!memcmp(wfx
, This
->wfx
, sizeof(*wfx
)) && !memcmp(wfx
+ 1, This
->wfx
+ 1, wfx
->cbSize
))
670 LeaveCriticalSection(&This
->cs
);
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
= malloc(sizeof(*wfx
) + wfx
->cbSize
)))
690 LeaveCriticalSection(&This
->cs
);
691 return E_OUTOFMEMORY
;
693 memcpy(new_wfx
, wfx
, sizeof(*wfx
) + wfx
->cbSize
);
697 LeaveCriticalSection(&This
->cs
);
702 static HRESULT WINAPI
mmsysaudio_GetStatus(ISpMMSysAudio
*iface
, SPAUDIOSTATUS
*status
)
704 FIXME("(%p, %p): stub.\n", iface
, status
);
709 static HRESULT WINAPI
mmsysaudio_SetBufferInfo(ISpMMSysAudio
*iface
, const SPAUDIOBUFFERINFO
*info
)
711 FIXME("(%p, %p): stub.\n", iface
, info
);
716 static HRESULT WINAPI
mmsysaudio_GetBufferInfo(ISpMMSysAudio
*iface
, SPAUDIOBUFFERINFO
*info
)
718 FIXME("(%p, %p): stub.\n", iface
, info
);
723 static HRESULT WINAPI
mmsysaudio_GetDefaultFormat(ISpMMSysAudio
*iface
, GUID
*guid
, WAVEFORMATEX
**wfx
)
725 FIXME("(%p, %p, %p): stub.\n", iface
, guid
, wfx
);
730 static HANDLE WINAPI
mmsysaudio_EventHandle(ISpMMSysAudio
*iface
)
732 struct mmaudio
*This
= impl_from_ISpMMSysAudio(iface
);
734 TRACE("(%p).\n", iface
);
739 static HRESULT WINAPI
mmsysaudio_GetVolumeLevel(ISpMMSysAudio
*iface
, ULONG
*level
)
741 FIXME("(%p, %p): stub.\n", iface
, level
);
746 static HRESULT WINAPI
mmsysaudio_SetVolumeLevel(ISpMMSysAudio
*iface
, ULONG level
)
748 FIXME("(%p, %lu): stub.\n", iface
, level
);
753 static HRESULT WINAPI
mmsysaudio_GetBufferNotifySize(ISpMMSysAudio
*iface
, ULONG
*size
)
755 FIXME("(%p, %p): stub.\n", iface
, size
);
760 static HRESULT WINAPI
mmsysaudio_SetBufferNotifySize(ISpMMSysAudio
*iface
, ULONG size
)
762 FIXME("(%p, %lu): stub.\n", iface
, size
);
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
);
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())
791 EnterCriticalSection(&This
->cs
);
793 if (id
== This
->device_id
)
795 LeaveCriticalSection(&This
->cs
);
798 if (This
->state
!= SPAS_CLOSED
)
800 LeaveCriticalSection(&This
->cs
);
801 return SPERR_DEVICE_BUSY
;
803 This
->device_id
= id
;
805 LeaveCriticalSection(&This
->cs
);
810 static HRESULT WINAPI
mmsysaudio_GetMMHandle(ISpMMSysAudio
*iface
, void **handle
)
812 FIXME("(%p, %p): stub.\n", iface
, handle
);
817 static HRESULT WINAPI
mmsysaudio_GetLineId(ISpMMSysAudio
*iface
, UINT
*id
)
819 FIXME("(%p, %p): stub.\n", iface
, id
);
824 static HRESULT WINAPI
mmsysaudio_SetLineId(ISpMMSysAudio
*iface
, UINT id
)
826 FIXME("(%p, %u): stub.\n", iface
, id
);
831 static const ISpMMSysAudioVtbl mmsysaudio_vtbl
=
833 mmsysaudio_QueryInterface
,
843 mmsysaudio_LockRegion
,
844 mmsysaudio_UnlockRegion
,
847 mmsysaudio_GetFormat
,
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
,
866 static HRESULT
mmaudio_create(IUnknown
*outer
, REFIID iid
, void **obj
, enum flow_type flow
)
868 struct mmaudio
*This
;
871 if (flow
!= FLOW_OUT
)
873 FIXME("flow %d not implemented.\n", flow
);
877 if (!(This
= calloc(1, 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
;
887 This
->device_id
= WAVE_MAPPER
;
888 This
->state
= SPAS_CLOSED
;
890 if (!(This
->wfx
= malloc(sizeof(*This
->wfx
))))
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
);
914 HRESULT
mmaudio_out_create(IUnknown
*outer
, REFIID iid
, void **obj
)
916 return mmaudio_create(outer
, iid
, obj
, FLOW_OUT
);