2 * Copyright 2010 Maarten Lankhorst for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define NONAMELESSUNION
27 #elif defined(HAVE_OPENAL_AL_H)
28 #include <OpenAL/al.h>
29 #include <OpenAL/alc.h>
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
40 #include "mmdeviceapi.h"
43 #include "audioclient.h"
44 #include "endpointvolume.h"
45 #include "audiopolicy.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi
);
53 typedef struct ACRender ACRender
;
54 typedef struct ACCapture ACCapture
;
55 typedef struct ACSession ACSession
;
56 typedef struct ASVolume ASVolume
;
57 typedef struct AClock AClock
;
59 typedef struct ACImpl
{
60 const IAudioClientVtbl
*lpVtbl
;
65 CRITICAL_SECTION
*crst
;
67 DWORD locked
, flags
, candisconnect
;
68 DWORD bufsize_frames
, ofs_frames
, periodsize_frames
, pad
, padpartial
;
73 REFERENCE_TIME laststamp
;
86 const IAudioRenderClientVtbl
*lpVtbl
;
92 const IAudioCaptureClientVtbl
*lpVtbl
;
98 const IAudioSessionControl2Vtbl
*lpVtbl
;
104 const ISimpleAudioVolumeVtbl
*lpVtbl
;
110 const IAudioClockVtbl
*lpVtbl
;
111 const IAudioClock2Vtbl
*lp2Vtbl
;
116 static const IAudioClientVtbl ACImpl_Vtbl
;
117 static const IAudioRenderClientVtbl ACRender_Vtbl
;
118 static const IAudioCaptureClientVtbl ACCapture_Vtbl
;
119 static const IAudioSessionControl2Vtbl ACSession_Vtbl
;
120 static const ISimpleAudioVolumeVtbl ASVolume_Vtbl
;
121 static const IAudioClockVtbl AClock_Vtbl
;
122 static const IAudioClock2Vtbl AClock2_Vtbl
;
124 static HRESULT
AudioRenderClient_Create(ACImpl
*parent
, ACRender
**ppv
);
125 static void AudioRenderClient_Destroy(ACRender
*This
);
126 static HRESULT
AudioCaptureClient_Create(ACImpl
*parent
, ACCapture
**ppv
);
127 static void AudioCaptureClient_Destroy(ACCapture
*This
);
128 static HRESULT
AudioSessionControl_Create(ACImpl
*parent
, ACSession
**ppv
);
129 static void AudioSessionControl_Destroy(ACSession
*This
);
130 static HRESULT
AudioSimpleVolume_Create(ACImpl
*parent
, ASVolume
**ppv
);
131 static void AudioSimpleVolume_Destroy(ASVolume
*This
);
132 static HRESULT
AudioClock_Create(ACImpl
*parent
, AClock
**ppv
);
133 static void AudioClock_Destroy(AClock
*This
);
135 static int valid_dev(ACImpl
*This
)
139 if (This
->parent
->flow
== eRender
&& This
->dev
!= This
->parent
->device
)
144 static int get_format_PCM(WAVEFORMATEX
*format
)
146 if (format
->nChannels
> 2) {
147 FIXME("nChannels > 2 not documented for WAVE_FORMAT_PCM!\n");
153 if (format
->nBlockAlign
!= format
->wBitsPerSample
/8*format
->nChannels
) {
154 WARN("Invalid nBlockAlign %u, from %u %u\n",
155 format
->nBlockAlign
, format
->wBitsPerSample
, format
->nChannels
);
159 switch (format
->wBitsPerSample
) {
161 switch (format
->nChannels
) {
162 case 1: return AL_FORMAT_MONO8
;
163 case 2: return AL_FORMAT_STEREO8
;
167 switch (format
->nChannels
) {
168 case 1: return AL_FORMAT_MONO16
;
169 case 2: return AL_FORMAT_STEREO16
;
174 if (!(format
->wBitsPerSample
% 8))
175 WARN("Could not get OpenAL format (%d-bit, %d channels)\n",
176 format
->wBitsPerSample
, format
->nChannels
);
180 /* Speaker configs */
181 #define MONO SPEAKER_FRONT_CENTER
182 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
183 #define REAR (SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
184 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
185 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
186 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
187 #define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
189 static int get_format_EXT(WAVEFORMATEX
*format
)
191 WAVEFORMATEXTENSIBLE
*wfe
;
193 if(format
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
)-sizeof(WAVEFORMATEX
)) {
194 WARN("Invalid cbSize specified for WAVE_FORMAT_EXTENSIBLE (%d)\n", format
->cbSize
);
198 wfe
= (WAVEFORMATEXTENSIBLE
*)format
;
199 wfe
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
)-sizeof(WAVEFORMATEX
);
200 if (wfe
->Samples
.wValidBitsPerSample
&&
201 wfe
->Samples
.wValidBitsPerSample
!= format
->wBitsPerSample
) {
202 FIXME("wValidBitsPerSample(%u) != wBitsPerSample(%u) unsupported\n",
203 wfe
->Samples
.wValidBitsPerSample
, format
->wBitsPerSample
);
207 TRACE("Extensible values:\n"
209 " ChannelMask = 0x%08x\n"
211 wfe
->Samples
.wReserved
, wfe
->dwChannelMask
,
212 debugstr_guid(&wfe
->SubFormat
));
214 if (wfe
->dwChannelMask
!= MONO
215 && wfe
->dwChannelMask
!= STEREO
216 && !palIsExtensionPresent("AL_EXT_MCFORMATS")) {
217 /* QUAD PCM might still work, special case */
218 if (palIsExtensionPresent("AL_LOKI_quadriphonic")
219 && IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)
220 && wfe
->dwChannelMask
== QUAD
) {
221 if (format
->wBitsPerSample
== 16)
222 return AL_FORMAT_QUAD16_LOKI
;
223 else if (format
->wBitsPerSample
== 8)
224 return AL_FORMAT_QUAD8_LOKI
;
226 WARN("Not all formats available\n");
230 if(IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
)) {
231 if (format
->wBitsPerSample
== 8) {
232 switch (wfe
->dwChannelMask
) {
233 case MONO
: return AL_FORMAT_MONO8
;
234 case STEREO
: return AL_FORMAT_STEREO8
;
235 case REAR
: return AL_FORMAT_REAR8
;
236 case QUAD
: return AL_FORMAT_QUAD8
;
237 case X5DOT1
: return AL_FORMAT_51CHN8
;
238 case X6DOT1
: return AL_FORMAT_61CHN8
;
239 case X7DOT1
: return AL_FORMAT_71CHN8
;
242 } else if (format
->wBitsPerSample
== 16) {
243 switch (wfe
->dwChannelMask
) {
244 case MONO
: return AL_FORMAT_MONO16
;
245 case STEREO
: return AL_FORMAT_STEREO16
;
246 case REAR
: return AL_FORMAT_REAR16
;
247 case QUAD
: return AL_FORMAT_QUAD16
;
248 case X5DOT1
: return AL_FORMAT_51CHN16
;
249 case X6DOT1
: return AL_FORMAT_61CHN16
;
250 case X7DOT1
: return AL_FORMAT_71CHN16
;
254 else if (!(format
->wBitsPerSample
% 8))
255 ERR("Could not get OpenAL PCM format (%d-bit, mask 0x%08x)\n",
256 format
->wBitsPerSample
, wfe
->dwChannelMask
);
259 else if(IsEqualGUID(&wfe
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)) {
260 if (format
->wBitsPerSample
!= 32) {
261 WARN("Invalid valid bits %u/32\n", format
->wBitsPerSample
);
264 switch (wfe
->dwChannelMask
) {
265 case MONO
: return AL_FORMAT_MONO_FLOAT32
;
266 case STEREO
: return AL_FORMAT_STEREO_FLOAT32
;
267 case REAR
: return AL_FORMAT_REAR32
;
268 case QUAD
: return AL_FORMAT_QUAD32
;
269 case X5DOT1
: return AL_FORMAT_51CHN32
;
270 case X6DOT1
: return AL_FORMAT_61CHN32
;
271 case X7DOT1
: return AL_FORMAT_71CHN32
;
273 ERR("Could not get OpenAL float format (%d-bit, mask 0x%08x)\n",
274 format
->wBitsPerSample
, wfe
->dwChannelMask
);
278 else if (!IsEqualGUID(&wfe
->SubFormat
, &GUID_NULL
))
279 ERR("Unhandled extensible format: %s\n", debugstr_guid(&wfe
->SubFormat
));
283 static ALint
get_format(WAVEFORMATEX
*in
)
286 if (in
->wFormatTag
== WAVE_FORMAT_PCM
)
287 ret
= get_format_PCM(in
);
288 else if (in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
289 ret
= get_format_EXT(in
);
293 static REFERENCE_TIME
gettime(void) {
294 LARGE_INTEGER stamp
, freq
;
295 QueryPerformanceCounter(&stamp
);
296 QueryPerformanceFrequency(&freq
);
297 return (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
300 HRESULT
AudioClient_Create(MMDevice
*parent
, IAudioClient
**ppv
)
302 ACImpl
*This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*This
));
303 *ppv
= (IAudioClient
*)This
;
305 return E_OUTOFMEMORY
;
306 This
->crst
= &parent
->crst
;
307 This
->lpVtbl
= &ACImpl_Vtbl
;
309 This
->parent
= parent
;
313 static void AudioClient_Destroy(ACImpl
*This
)
317 DeleteTimerQueueTimer(NULL
, This
->timer_id
, INVALID_HANDLE_VALUE
);
321 AudioRenderClient_Destroy(This
->render
);
323 AudioCaptureClient_Destroy(This
->capture
);
325 AudioSessionControl_Destroy(This
->session
);
327 AudioSimpleVolume_Destroy(This
->svolume
);
329 AudioClock_Destroy(This
->clock
);
330 if (!valid_dev(This
))
331 TRACE("Not destroying device since none exists\n");
332 else if (This
->parent
->flow
== eRender
) {
333 setALContext(This
->parent
->ctx
);
334 IAudioClient_Stop((IAudioClient
*)This
);
335 IAudioClient_Reset((IAudioClient
*)This
);
336 palDeleteSources(1, &This
->source
);
340 else if (This
->parent
->flow
== eCapture
)
341 palcCaptureCloseDevice(This
->dev
);
342 HeapFree(GetProcessHeap(), 0, This
->pwfx
);
343 HeapFree(GetProcessHeap(), 0, This
->buffer
);
344 HeapFree(GetProcessHeap(), 0, This
);
347 static void CALLBACK
AC_tick(void *data
, BOOLEAN fired
)
352 EnterCriticalSection(This
->crst
);
354 IAudioClient_GetCurrentPadding((IAudioClient
*)This
, &pad
);
355 LeaveCriticalSection(This
->crst
);
358 /* Open device and set/update internal mixing format based on information
359 * openal provides us. if the device cannot be opened, assume 48khz
360 * Guessing the frequency is harmless, since if GetMixFormat fails to open
361 * the device, then Initialize will likely fail as well
363 static HRESULT
AC_OpenRenderAL(ACImpl
*This
)
365 char alname
[MAX_PATH
];
366 MMDevice
*cur
= This
->parent
;
368 alname
[sizeof(alname
)-1] = 0;
370 return cur
->ctx
? S_OK
: AUDCLNT_E_SERVICE_NOT_RUNNING
;
372 WideCharToMultiByte(CP_UNIXCP
, 0, cur
->alname
, -1,
373 alname
, sizeof(alname
)/sizeof(*alname
)-1, NULL
, NULL
);
374 cur
->device
= palcOpenDevice(alname
);
376 ALCenum err
= palcGetError(NULL
);
377 WARN("Could not open device %s: 0x%04x\n", alname
, err
);
378 return AUDCLNT_E_DEVICE_IN_USE
;
380 cur
->ctx
= palcCreateContext(cur
->device
, NULL
);
382 ALCenum err
= palcGetError(cur
->device
);
383 ERR("Could not create context: 0x%04x\n", err
);
384 return AUDCLNT_E_SERVICE_NOT_RUNNING
;
387 return AUDCLNT_E_DEVICE_IN_USE
;
391 static HRESULT
AC_OpenCaptureAL(ACImpl
*This
)
393 char alname
[MAX_PATH
];
394 ALint freq
, size_frames
;
396 freq
= This
->pwfx
->nSamplesPerSec
;
397 size_frames
= This
->bufsize_frames
;
399 alname
[sizeof(alname
)-1] = 0;
401 FIXME("Attempting to open device while already open\n");
404 WideCharToMultiByte(CP_UNIXCP
, 0, This
->parent
->alname
, -1,
405 alname
, sizeof(alname
)/sizeof(*alname
)-1, NULL
, NULL
);
406 This
->dev
= palcCaptureOpenDevice(alname
, freq
, This
->format
, size_frames
);
408 ALCenum err
= palcGetError(NULL
);
409 FIXME("Could not open device %s with buf size %u: 0x%04x\n",
410 alname
, This
->bufsize_frames
, err
);
411 return AUDCLNT_E_DEVICE_IN_USE
;
416 static HRESULT WINAPI
AC_QueryInterface(IAudioClient
*iface
, REFIID riid
, void **ppv
)
418 TRACE("(%p)->(%s,%p)\n", iface
, debugstr_guid(riid
), ppv
);
423 if (IsEqualIID(riid
, &IID_IUnknown
)
424 || IsEqualIID(riid
, &IID_IAudioClient
))
427 IUnknown_AddRef((IUnknown
*)*ppv
);
430 WARN("Unknown interface %s\n", debugstr_guid(riid
));
431 return E_NOINTERFACE
;
434 static ULONG WINAPI
AC_AddRef(IAudioClient
*iface
)
436 ACImpl
*This
= (ACImpl
*)iface
;
438 ref
= InterlockedIncrement(&This
->ref
);
439 TRACE("Refcount now %i\n", ref
);
443 static ULONG WINAPI
AC_Release(IAudioClient
*iface
)
445 ACImpl
*This
= (ACImpl
*)iface
;
447 ref
= InterlockedDecrement(&This
->ref
);
448 TRACE("Refcount now %i\n", ref
);
450 AudioClient_Destroy(This
);
454 static HRESULT WINAPI
AC_Initialize(IAudioClient
*iface
, AUDCLNT_SHAREMODE mode
, DWORD flags
, REFERENCE_TIME duration
, REFERENCE_TIME period
, const WAVEFORMATEX
*pwfx
, const GUID
*sessionguid
)
456 ACImpl
*This
= (ACImpl
*)iface
;
462 TRACE("(%p)->(%x,%x,%u,%u,%p,%s)\n", This
, mode
, flags
, (int)duration
, (int)period
, pwfx
, debugstr_guid(sessionguid
));
464 return AUDCLNT_E_ALREADY_INITIALIZED
;
465 if (mode
!= AUDCLNT_SHAREMODE_SHARED
466 && mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
) {
467 WARN("Unknown mode %x\n", mode
);
468 return AUDCLNT_E_NOT_INITIALIZED
;
471 if (flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
472 |AUDCLNT_STREAMFLAGS_LOOPBACK
473 |AUDCLNT_STREAMFLAGS_EVENTCALLBACK
474 |AUDCLNT_STREAMFLAGS_NOPERSIST
475 |AUDCLNT_STREAMFLAGS_RATEADJUST
476 |AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
477 |AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
478 |AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
)) {
479 WARN("Unknown flags 0x%08x\n", flags
);
483 WARN("Flags 0x%08x ignored\n", flags
);
487 WARN("Session guid %s ignored\n", debugstr_guid(sessionguid
));
489 hr
= IAudioClient_IsFormatSupported(iface
, mode
, pwfx
, &pwfx2
);
490 CoTaskMemFree(pwfx2
);
491 if (FAILED(hr
) || pwfx2
) {
492 WARN("Format not supported, or had to be modified!\n");
493 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
495 EnterCriticalSection(This
->crst
);
496 HeapFree(GetProcessHeap(), 0, This
->pwfx
);
497 This
->pwfx
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pwfx
) + pwfx
->cbSize
);
502 memcpy(This
->pwfx
, pwfx
, sizeof(*pwfx
) + pwfx
->cbSize
);
503 if (pwfx
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) {
504 WAVEFORMATEXTENSIBLE
*wfe
= (WAVEFORMATEXTENSIBLE
*)This
->pwfx
;
505 switch (pwfx
->nChannels
) {
506 case 1: wfe
->dwChannelMask
= MONO
; break;
507 case 2: wfe
->dwChannelMask
= STEREO
; break;
508 case 4: wfe
->dwChannelMask
= QUAD
; break;
509 case 6: wfe
->dwChannelMask
= X5DOT1
; break;
510 case 7: wfe
->dwChannelMask
= X6DOT1
; break;
511 case 8: wfe
->dwChannelMask
= X7DOT1
; break;
513 ERR("How did we end up with %i channels?\n", pwfx
->nChannels
);
514 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
519 hr
= IAudioClient_GetDevicePeriod(iface
, &time
, NULL
);
523 This
->periodsize_frames
= (DWORD64
)This
->pwfx
->nSamplesPerSec
* time
/ (DWORD64
)10000000;
524 if (duration
> 20000000)
527 This
->bufsize_frames
= duration
/ time
* This
->periodsize_frames
;
529 This
->bufsize_frames
+= This
->periodsize_frames
;
530 bufsize_bytes
= This
->bufsize_frames
* pwfx
->nBlockAlign
;
532 This
->format
= get_format(This
->pwfx
);
533 if (This
->parent
->flow
== eRender
) {
535 ALuint buf
= 0, towrite
;
537 hr
= AC_OpenRenderAL(This
);
538 This
->dev
= This
->parent
->device
;
542 /* Test the returned format */
543 towrite
= sizeof(silence
);
544 towrite
-= towrite
% This
->pwfx
->nBlockAlign
;
545 if (This
->pwfx
->wBitsPerSample
!= 8)
546 memset(silence
, 0, sizeof(silence
));
548 memset(silence
, 128, sizeof(silence
));
549 setALContext(This
->parent
->ctx
);
551 palGenBuffers(1, &buf
);
552 palBufferData(buf
, This
->format
, silence
, towrite
, This
->pwfx
->nSamplesPerSec
);
553 palDeleteBuffers(1, &buf
);
555 hr
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
556 else if (!This
->source
) {
557 palGenSources(1, &This
->source
);
558 palSourcei(This
->source
, AL_LOOPING
, AL_FALSE
);
564 hr
= AC_OpenCaptureAL(This
);
569 This
->candisconnect
= palcIsExtensionPresent(This
->dev
, "ALC_EXT_disconnect");
570 This
->buffer
= HeapAlloc(GetProcessHeap(), 0, bufsize_bytes
);
577 This
->running
= FALSE
;
580 LeaveCriticalSection(This
->crst
);
584 static HRESULT WINAPI
AC_GetBufferSize(IAudioClient
*iface
, UINT32
*frames
)
586 ACImpl
*This
= (ACImpl
*)iface
;
587 TRACE("(%p)->(%p)\n", This
, frames
);
589 return AUDCLNT_E_NOT_INITIALIZED
;
592 *frames
= This
->bufsize_frames
;
596 static HRESULT WINAPI
AC_GetStreamLatency(IAudioClient
*iface
, REFERENCE_TIME
*latency
)
598 ACImpl
*This
= (ACImpl
*)iface
;
599 TRACE("(%p)->(%p)\n", This
, latency
);
602 return AUDCLNT_E_NOT_INITIALIZED
;
612 static int disconnected(ACImpl
*This
)
614 if (!This
->candisconnect
)
616 if (This
->parent
->flow
== eRender
) {
617 if (This
->parent
->device
) {
619 palcGetIntegerv(This
->parent
->device
, ALC_CONNECTED
, 1, &con
);
620 palcGetError(This
->parent
->device
);
622 palcCloseDevice(This
->parent
->device
);
623 This
->parent
->device
= NULL
;
624 This
->parent
->ctx
= NULL
;
629 if (!This
->parent
->device
&& FAILED(AC_OpenRenderAL(This
))) {
630 This
->pad
-= This
->padpartial
;
631 This
->padpartial
= 0;
634 if (This
->parent
->device
!= This
->dev
) {
635 WARN("Emptying buffer after newly reconnected!\n");
636 This
->pad
-= This
->padpartial
;
637 This
->padpartial
= 0;
639 This
->dev
= This
->parent
->device
;
640 setALContext(This
->parent
->ctx
);
641 palGenSources(1, &This
->source
);
642 palSourcei(This
->source
, AL_LOOPING
, AL_FALSE
);
645 if (This
->render
&& !This
->locked
&& This
->pad
) {
646 UINT pad
= This
->pad
;
650 /* Probably will cause sound glitches, who cares? */
651 IAudioRenderClient_GetBuffer((IAudioRenderClient
*)This
->render
, pad
, &data
);
652 IAudioRenderClient_ReleaseBuffer((IAudioRenderClient
*)This
->render
, pad
, 0);
659 palcGetIntegerv(This
->dev
, ALC_CONNECTED
, 1, &con
);
660 palcGetError(This
->dev
);
662 palcCaptureCloseDevice(This
->dev
);
668 if (FAILED(AC_OpenCaptureAL(This
)))
671 WARN("Emptying buffer after newly reconnected!\n");
672 This
->pad
= This
->ofs_frames
= 0;
674 palcCaptureStart(This
->dev
);
680 static HRESULT WINAPI
AC_GetCurrentPadding(IAudioClient
*iface
, UINT32
*numpad
)
682 ACImpl
*This
= (ACImpl
*)iface
;
685 TRACE("(%p)->(%p)\n", This
, numpad
);
687 return AUDCLNT_E_NOT_INITIALIZED
;
690 EnterCriticalSection(This
->crst
);
691 if (disconnected(This
)) {
692 REFERENCE_TIME time
= gettime(), period
;
694 WARN("No device found, faking increment\n");
695 IAudioClient_GetDevicePeriod(iface
, &period
, NULL
);
696 while (This
->running
&& time
- This
->laststamp
>= period
) {
697 This
->laststamp
+= period
;
699 if (This
->parent
->flow
== eCapture
) {
700 This
->pad
+= This
->periodsize_frames
;
701 if (This
->pad
> This
->bufsize_frames
)
702 This
->pad
= This
->bufsize_frames
;
704 if (This
->pad
<= This
->periodsize_frames
) {
708 This
->pad
-= This
->periodsize_frames
;
712 if (This
->parent
->flow
== eCapture
)
713 *numpad
= This
->pad
>= This
->periodsize_frames
? This
->periodsize_frames
: 0;
716 } else if (This
->parent
->flow
== eRender
) {
718 ALint state
, padpart
;
719 setALContext(This
->parent
->ctx
);
721 palGetSourcei(This
->source
, AL_BYTE_OFFSET
, &padpart
);
722 palGetSourcei(This
->source
, AL_SOURCE_STATE
, &state
);
723 padpart
/= This
->pwfx
->nBlockAlign
;
724 if (state
== AL_STOPPED
&& This
->running
)
726 if (This
->running
&& This
->padpartial
!= padpart
) {
727 This
->padpartial
= padpart
;
728 This
->laststamp
= gettime();
729 #if 0 /* Manipulative lie */
730 } else if (This
->running
) {
731 ALint size
= This
->pad
- padpart
;
732 if (size
> This
->periodsize_frames
)
733 size
= This
->periodsize_frames
;
734 played
= (gettime() - This
->laststamp
)*8;
735 played
= played
* This
->pwfx
->nSamplesPerSec
/ 10000000;
740 *numpad
= This
->pad
- This
->padpartial
- played
;
741 if (This
->handle
&& (*numpad
+ This
->periodsize_frames
) <= This
->bufsize_frames
)
742 SetEvent(This
->handle
);
746 DWORD block
= This
->pwfx
->nBlockAlign
;
747 palcGetIntegerv(This
->dev
, ALC_CAPTURE_SAMPLES
, 1, &avail
);
749 DWORD ofs_frames
= This
->ofs_frames
+ This
->pad
;
751 ofs_frames
%= This
->bufsize_frames
;
752 buf1
= This
->buffer
+ (ofs_frames
* block
);
753 This
->laststamp
= gettime();
755 SetEvent(This
->handle
);
757 if (ofs_frames
+ avail
<= This
->bufsize_frames
)
758 palcCaptureSamples(This
->dev
, buf1
, avail
);
760 DWORD part1
= This
->bufsize_frames
- ofs_frames
;
761 palcCaptureSamples(This
->dev
, buf1
, part1
);
762 palcCaptureSamples(This
->dev
, This
->buffer
, avail
- part1
);
765 This
->frameswritten
+= avail
;
766 /* Increase ofs if the app forgets to read */
767 if (This
->pad
> This
->bufsize_frames
) {
769 WARN("Overflowed! %u frames\n", This
->pad
- This
->bufsize_frames
);
770 This
->ofs_frames
+= This
->pad
- This
->bufsize_frames
;
771 rest
= This
->ofs_frames
% This
->periodsize_frames
;
773 This
->ofs_frames
+= This
->periodsize_frames
- rest
;
774 This
->ofs_frames
%= This
->bufsize_frames
;
775 This
->pad
= This
->bufsize_frames
;
778 if (This
->pad
>= This
->periodsize_frames
)
779 *numpad
= This
->periodsize_frames
;
783 LeaveCriticalSection(This
->crst
);
785 TRACE("%u queued\n", *numpad
);
789 static HRESULT WINAPI
AC_IsFormatSupported(IAudioClient
*iface
, AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*pwfx
, WAVEFORMATEX
**outpwfx
)
791 ACImpl
*This
= (ACImpl
*)iface
;
795 TRACE("(%p)->(%x,%p,%p)\n", This
, mode
, pwfx
, outpwfx
);
799 if (mode
== AUDCLNT_SHAREMODE_SHARED
&& !outpwfx
)
801 if (mode
!= AUDCLNT_SHAREMODE_SHARED
802 && mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
) {
803 WARN("Unknown mode %x\n", mode
);
807 if (pwfx
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
808 size
= sizeof(WAVEFORMATEXTENSIBLE
);
809 else if (pwfx
->wFormatTag
== WAVE_FORMAT_PCM
)
810 size
= sizeof(WAVEFORMATEX
);
812 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
814 if (pwfx
->nSamplesPerSec
< 8000
815 || pwfx
->nSamplesPerSec
> 192000)
816 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
817 if (pwfx
->wFormatTag
!= WAVE_FORMAT_EXTENSIBLE
818 || !IsEqualIID(&((WAVEFORMATEXTENSIBLE
*)pwfx
)->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)) {
819 if (pwfx
->wBitsPerSample
> 16)
820 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
823 switch (pwfx
->nChannels
) {
824 case 1: mask
= MONO
; break;
825 case 2: mask
= STEREO
; break;
826 case 4: mask
= QUAD
; break;
827 case 6: mask
= X5DOT1
; break;
828 case 7: mask
= X6DOT1
; break;
829 case 8: mask
= X7DOT1
; break;
831 TRACE("Unsupported channel count %i\n", pwfx
->nChannels
);
832 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
834 tmp
= CoTaskMemAlloc(size
);
838 return E_OUTOFMEMORY
;
840 memcpy(tmp
, pwfx
, size
);
841 tmp
->nBlockAlign
= tmp
->nChannels
* tmp
->wBitsPerSample
/ 8;
842 tmp
->nAvgBytesPerSec
= tmp
->nBlockAlign
* tmp
->nSamplesPerSec
;
843 tmp
->cbSize
= size
- sizeof(WAVEFORMATEX
);
844 if (tmp
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) {
845 WAVEFORMATEXTENSIBLE
*ex
= (WAVEFORMATEXTENSIBLE
*)tmp
;
847 if (ex
->Samples
.wValidBitsPerSample
)
848 ex
->Samples
.wValidBitsPerSample
= ex
->Format
.wBitsPerSample
;
850 /* Rear is a special allowed case */
851 if (ex
->dwChannelMask
852 && !(ex
->Format
.nChannels
== 2 && ex
->dwChannelMask
== REAR
))
853 ex
->dwChannelMask
= mask
;
856 if (memcmp(pwfx
, tmp
, size
)) {
860 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
868 static HRESULT WINAPI
AC_GetMixFormat(IAudioClient
*iface
, WAVEFORMATEX
**pwfx
)
870 ACImpl
*This
= (ACImpl
*)iface
;
871 PROPVARIANT pv
= { VT_EMPTY
};
874 TRACE("(%p)->(%p)\n", This
, pwfx
);
878 hr
= MMDevice_GetPropValue(&This
->parent
->devguid
, This
->parent
->flow
,
879 &PKEY_AudioEngine_DeviceFormat
, &pv
);
880 *pwfx
= (WAVEFORMATEX
*)pv
.u
.blob
.pBlobData
;
881 if (SUCCEEDED(hr
) && pv
.vt
== VT_EMPTY
)
884 TRACE("Returning 0x%08x\n", hr
);
888 static HRESULT WINAPI
AC_GetDevicePeriod(IAudioClient
*iface
, REFERENCE_TIME
*defperiod
, REFERENCE_TIME
*minperiod
)
890 ACImpl
*This
= (ACImpl
*)iface
;
892 TRACE("(%p)->(%p)\n", This
, minperiod
);
893 if (!defperiod
&& !minperiod
)
903 static HRESULT WINAPI
AC_Start(IAudioClient
*iface
)
905 ACImpl
*This
= (ACImpl
*)iface
;
907 REFERENCE_TIME refresh
;
909 TRACE("(%p)\n", This
);
911 return AUDCLNT_E_NOT_INITIALIZED
;
912 if (This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) {
914 return AUDCLNT_E_EVENTHANDLE_NOT_SET
;
915 FIXME("Event handles not fully tested\n");
917 EnterCriticalSection(This
->crst
);
919 hr
= AUDCLNT_E_NOT_STOPPED
;
922 if (!valid_dev(This
))
923 WARN("No valid device\n");
924 else if (This
->parent
->flow
== eRender
) {
925 setALContext(This
->parent
->ctx
);
926 palSourcePlay(This
->source
);
931 palcCaptureStart(This
->dev
);
933 AC_GetDevicePeriod(iface
, &refresh
, NULL
);
934 if (!This
->timer_id
&& This
->handle
)
935 CreateTimerQueueTimer(&This
->timer_id
, NULL
, AC_tick
, This
,
936 refresh
/ 20000, refresh
/ 20000,
937 WT_EXECUTEINTIMERTHREAD
);
938 /* Set to 0, otherwise risk running the clock backwards
939 * This will cause AudioClock::GetPosition to return the maximum
940 * possible value for the current buffer
943 This
->running
= TRUE
;
946 LeaveCriticalSection(This
->crst
);
950 static HRESULT WINAPI
AC_Stop(IAudioClient
*iface
)
952 ACImpl
*This
= (ACImpl
*)iface
;
954 TRACE("(%p)\n", This
);
956 return AUDCLNT_E_NOT_INITIALIZED
;
959 EnterCriticalSection(This
->crst
);
960 if (!valid_dev(This
))
961 WARN("No valid device\n");
962 else if (This
->parent
->flow
== eRender
) {
964 setALContext(This
->parent
->ctx
);
965 palSourcePause(This
->source
);
968 palGetSourcei(This
->source
, AL_SOURCE_STATE
, &state
);
969 if (state
!= AL_PLAYING
)
977 palcCaptureStop(This
->dev
);
978 This
->running
= FALSE
;
979 timer_id
= This
->timer_id
;
981 LeaveCriticalSection(This
->crst
);
983 DeleteTimerQueueTimer(NULL
, timer_id
, INVALID_HANDLE_VALUE
);
987 static HRESULT WINAPI
AC_Reset(IAudioClient
*iface
)
989 ACImpl
*This
= (ACImpl
*)iface
;
991 TRACE("(%p)\n", This
);
993 return AUDCLNT_E_NOT_INITIALIZED
;
995 return AUDCLNT_E_NOT_STOPPED
;
996 EnterCriticalSection(This
->crst
);
998 hr
= AUDCLNT_E_BUFFER_OPERATION_PENDING
;
1001 if (!valid_dev(This
))
1002 WARN("No valid device\n");
1003 else if (This
->parent
->flow
== eRender
) {
1006 setALContext(This
->parent
->ctx
);
1007 palSourceStop(This
->source
);
1008 palGetSourcei(This
->source
, AL_BUFFERS_PROCESSED
, &n
);
1010 palSourceUnqueueBuffers(This
->source
, 1, &buf
);
1011 palDeleteBuffers(1, &buf
);
1017 palcGetIntegerv(This
->dev
, ALC_CAPTURE_SAMPLES
, 1, &avail
);
1019 palcCaptureSamples(This
->dev
, This
->buffer
, avail
);
1021 This
->pad
= This
->padpartial
= 0;
1022 This
->ofs_frames
= 0;
1023 This
->frameswritten
= 0;
1025 LeaveCriticalSection(This
->crst
);
1029 static HRESULT WINAPI
AC_SetEventHandle(IAudioClient
*iface
, HANDLE handle
)
1031 ACImpl
*This
= (ACImpl
*)iface
;
1032 TRACE("(%p)\n", This
);
1034 return AUDCLNT_E_NOT_INITIALIZED
;
1036 return E_INVALIDARG
;
1037 if (!(This
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
))
1038 return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
;
1039 This
->handle
= handle
;
1043 static HRESULT WINAPI
AC_GetService(IAudioClient
*iface
, REFIID riid
, void **ppv
)
1045 ACImpl
*This
= (ACImpl
*)iface
;
1047 TRACE("(%p)->(%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
1049 return AUDCLNT_E_NOT_INITIALIZED
;
1054 if (IsEqualIID(riid
, &IID_IAudioRenderClient
)) {
1055 if (This
->parent
->flow
!= eRender
)
1056 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1058 hr
= AudioRenderClient_Create(This
, &This
->render
);
1059 *ppv
= This
->render
;
1060 } else if (IsEqualIID(riid
, &IID_IAudioCaptureClient
)) {
1061 if (This
->parent
->flow
!= eCapture
)
1062 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
1064 hr
= AudioCaptureClient_Create(This
, &This
->capture
);
1065 *ppv
= This
->capture
;
1066 } else if (IsEqualIID(riid
, &IID_IAudioSessionControl
)) {
1068 hr
= AudioSessionControl_Create(This
, &This
->session
);
1069 *ppv
= This
->session
;
1070 } else if (IsEqualIID(riid
, &IID_ISimpleAudioVolume
)) {
1072 hr
= AudioSimpleVolume_Create(This
, &This
->svolume
);
1073 *ppv
= This
->svolume
;
1074 } else if (IsEqualIID(riid
, &IID_IAudioClock
)) {
1076 hr
= AudioClock_Create(This
, &This
->clock
);
1084 IUnknown_AddRef((IUnknown
*)*ppv
);
1088 FIXME("stub %s\n", debugstr_guid(riid
));
1089 return E_NOINTERFACE
;
1092 static const IAudioClientVtbl ACImpl_Vtbl
=
1099 AC_GetStreamLatency
,
1100 AC_GetCurrentPadding
,
1101 AC_IsFormatSupported
,
1111 static HRESULT
AudioRenderClient_Create(ACImpl
*parent
, ACRender
**ppv
)
1115 This
= *ppv
= HeapAlloc(GetProcessHeap(), 0, sizeof(*This
));
1117 return E_OUTOFMEMORY
;
1118 This
->lpVtbl
= &ACRender_Vtbl
;
1120 This
->parent
= parent
;
1121 AC_AddRef((IAudioClient
*)This
->parent
);
1125 static void AudioRenderClient_Destroy(ACRender
*This
)
1127 This
->parent
->render
= NULL
;
1128 AC_Release((IAudioClient
*)This
->parent
);
1129 HeapFree(GetProcessHeap(), 0, This
);
1132 static HRESULT WINAPI
ACR_QueryInterface(IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
1134 TRACE("(%p)->(%s,%p)\n", iface
, debugstr_guid(riid
), ppv
);
1139 if (IsEqualIID(riid
, &IID_IUnknown
)
1140 || IsEqualIID(riid
, &IID_IAudioRenderClient
))
1143 IUnknown_AddRef((IUnknown
*)*ppv
);
1146 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1147 return E_NOINTERFACE
;
1150 static ULONG WINAPI
ACR_AddRef(IAudioRenderClient
*iface
)
1152 ACRender
*This
= (ACRender
*)iface
;
1154 ref
= InterlockedIncrement(&This
->ref
);
1155 TRACE("Refcount now %i\n", ref
);
1159 static ULONG WINAPI
ACR_Release(IAudioRenderClient
*iface
)
1161 ACRender
*This
= (ACRender
*)iface
;
1163 ref
= InterlockedDecrement(&This
->ref
);
1164 TRACE("Refcount now %i\n", ref
);
1166 AudioRenderClient_Destroy(This
);
1170 static HRESULT WINAPI
ACR_GetBuffer(IAudioRenderClient
*iface
, UINT32 frames
, BYTE
**data
)
1172 ACRender
*This
= (ACRender
*)iface
;
1173 DWORD free
, framesize
;
1174 TRACE("(%p)->(%u,%p)\n", This
, frames
, data
);
1181 if (This
->parent
->locked
) {
1183 return AUDCLNT_E_OUT_OF_ORDER
;
1185 AC_GetCurrentPadding((IAudioClient
*)This
->parent
, &free
);
1186 if (This
->parent
->bufsize_frames
- free
< frames
) {
1187 ERR("Too large: %u %u %u\n", This
->parent
->bufsize_frames
, free
, frames
);
1188 return AUDCLNT_E_BUFFER_TOO_LARGE
;
1190 EnterCriticalSection(This
->parent
->crst
);
1191 This
->parent
->locked
= frames
;
1192 framesize
= This
->parent
->pwfx
->nBlockAlign
;
1194 /* Exact offset doesn't matter, offset could be 0 forever
1195 * but increasing it is easier to debug */
1196 if (This
->parent
->ofs_frames
+ frames
> This
->parent
->bufsize_frames
)
1197 This
->parent
->ofs_frames
= 0;
1198 *data
= This
->parent
->buffer
+ This
->parent
->ofs_frames
* framesize
;
1200 LeaveCriticalSection(This
->parent
->crst
);
1204 static HRESULT WINAPI
ACR_ReleaseBuffer(IAudioRenderClient
*iface
, UINT32 written_frames
, DWORD flags
)
1206 ACRender
*This
= (ACRender
*)iface
;
1207 BYTE
*buf
= This
->parent
->buffer
;
1208 DWORD framesize
= This
->parent
->pwfx
->nBlockAlign
;
1209 DWORD ofs_bytes
= This
->parent
->ofs_frames
* framesize
;
1210 DWORD written_bytes
= written_frames
* framesize
;
1211 DWORD freq
= This
->parent
->pwfx
->nSamplesPerSec
;
1212 DWORD bpp
= This
->parent
->pwfx
->wBitsPerSample
;
1215 TRACE("(%p)->(%u,%x)\n", This
, written_frames
, flags
);
1217 if (This
->parent
->locked
< written_frames
)
1218 return AUDCLNT_E_INVALID_SIZE
;
1220 if (flags
& ~AUDCLNT_BUFFERFLAGS_SILENT
)
1221 return E_INVALIDARG
;
1223 if (!written_frames
) {
1224 if (This
->parent
->locked
)
1225 FIXME("Handled right?\n");
1226 This
->parent
->locked
= 0;
1230 if (!This
->parent
->locked
)
1231 return AUDCLNT_E_OUT_OF_ORDER
;
1233 EnterCriticalSection(This
->parent
->crst
);
1235 This
->parent
->ofs_frames
+= written_frames
;
1236 This
->parent
->ofs_frames
%= This
->parent
->bufsize_frames
;
1237 This
->parent
->pad
+= written_frames
;
1238 This
->parent
->frameswritten
+= written_frames
;
1239 This
->parent
->locked
= 0;
1241 if (flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
1242 memset(buf
+ ofs_bytes
, bpp
!= 8 ? 0 : 128, written_bytes
);
1243 TRACE("buf: %p, ofs: %x, written %u, freq %u\n", buf
, ofs_bytes
, written_bytes
, freq
);
1244 if (!valid_dev(This
->parent
))
1247 setALContext(This
->parent
->parent
->ctx
);
1248 palGenBuffers(1, &albuf
);
1249 palBufferData(albuf
, This
->parent
->format
, buf
+ ofs_bytes
, written_bytes
, freq
);
1250 palSourceQueueBuffers(This
->parent
->source
, 1, &albuf
);
1251 TRACE("Queued %u\n", albuf
);
1253 if (This
->parent
->running
) {
1254 ALint state
= AL_PLAYING
, done
= 0, padpart
= 0;
1256 palGetSourcei(This
->parent
->source
, AL_BUFFERS_PROCESSED
, &done
);
1257 palGetSourcei(This
->parent
->source
, AL_BYTE_OFFSET
, &padpart
);
1258 palGetSourcei(This
->parent
->source
, AL_SOURCE_STATE
, &state
);
1259 padpart
/= framesize
;
1261 if (state
== AL_STOPPED
) {
1262 padpart
= This
->parent
->pad
;
1263 /* Buffer might have been processed in the small window
1264 * between first and third call */
1265 palGetSourcei(This
->parent
->source
, AL_BUFFERS_PROCESSED
, &done
);
1267 if (done
|| This
->parent
->padpartial
!= padpart
)
1268 This
->parent
->laststamp
= gettime();
1269 This
->parent
->padpartial
= padpart
;
1272 ALint size
, bits
, chan
;
1275 palSourceUnqueueBuffers(This
->parent
->source
, 1, &which
);
1276 palGetBufferi(which
, AL_SIZE
, &size
);
1277 palGetBufferi(which
, AL_BITS
, &bits
);
1278 palGetBufferi(which
, AL_CHANNELS
, &chan
);
1279 size
/= bits
* chan
/ 8;
1280 if (size
> This
->parent
->pad
) {
1282 size
= This
->parent
->pad
;
1284 This
->parent
->pad
-= size
;
1285 This
->parent
->padpartial
-= size
;
1286 TRACE("Unqueued %u\n", which
);
1287 palDeleteBuffers(1, &which
);
1290 if (state
!= AL_PLAYING
) {
1291 ERR("Starting from %x\n", state
);
1292 palSourcePlay(This
->parent
->source
);
1299 LeaveCriticalSection(This
->parent
->crst
);
1304 static const IAudioRenderClientVtbl ACRender_Vtbl
= {
1312 static HRESULT
AudioCaptureClient_Create(ACImpl
*parent
, ACCapture
**ppv
)
1315 This
= *ppv
= HeapAlloc(GetProcessHeap(), 0, sizeof(*This
));
1317 return E_OUTOFMEMORY
;
1318 This
->lpVtbl
= &ACCapture_Vtbl
;
1320 This
->parent
= parent
;
1321 AC_AddRef((IAudioClient
*)This
->parent
);
1325 static void AudioCaptureClient_Destroy(ACCapture
*This
)
1327 This
->parent
->capture
= NULL
;
1328 AC_Release((IAudioClient
*)This
->parent
);
1329 HeapFree(GetProcessHeap(), 0, This
);
1332 static HRESULT WINAPI
ACC_QueryInterface(IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
1334 TRACE("(%p)->(%s,%p)\n", iface
, debugstr_guid(riid
), ppv
);
1339 if (IsEqualIID(riid
, &IID_IUnknown
)
1340 || IsEqualIID(riid
, &IID_IAudioCaptureClient
))
1343 IUnknown_AddRef((IUnknown
*)*ppv
);
1346 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1347 return E_NOINTERFACE
;
1350 static ULONG WINAPI
ACC_AddRef(IAudioCaptureClient
*iface
)
1352 ACCapture
*This
= (ACCapture
*)iface
;
1354 ref
= InterlockedIncrement(&This
->ref
);
1355 TRACE("Refcount now %i\n", ref
);
1359 static ULONG WINAPI
ACC_Release(IAudioCaptureClient
*iface
)
1361 ACCapture
*This
= (ACCapture
*)iface
;
1363 ref
= InterlockedDecrement(&This
->ref
);
1364 TRACE("Refcount now %i\n", ref
);
1366 AudioCaptureClient_Destroy(This
);
1370 static HRESULT WINAPI
ACC_GetBuffer(IAudioCaptureClient
*iface
, BYTE
**data
, UINT32
*frames
, DWORD
*flags
, UINT64
*devpos
, UINT64
*qpcpos
)
1372 ACCapture
*This
= (ACCapture
*)iface
;
1374 DWORD block
= This
->parent
->pwfx
->nBlockAlign
;
1376 TRACE("(%p)->(%p,%p,%p,%p,%p)\n", This
, data
, frames
, flags
, devpos
, qpcpos
);
1383 FIXME("Flags can be null?\n");
1386 EnterCriticalSection(This
->parent
->crst
);
1387 hr
= AUDCLNT_E_OUT_OF_ORDER
;
1388 if (This
->parent
->locked
)
1390 IAudioCaptureClient_GetNextPacketSize(iface
, frames
);
1391 ofs
= This
->parent
->ofs_frames
;
1392 if ( ofs
% This
->parent
->periodsize_frames
)
1393 ERR("Unaligned offset %u with %u\n", ofs
, This
->parent
->periodsize_frames
);
1394 *data
= This
->parent
->buffer
+ ofs
* block
;
1395 This
->parent
->locked
= *frames
;
1397 *devpos
= This
->parent
->frameswritten
- This
->parent
->pad
;
1399 *qpcpos
= This
->parent
->laststamp
;
1403 hr
= AUDCLNT_S_BUFFER_EMPTY
;
1405 LeaveCriticalSection(This
->parent
->crst
);
1406 TRACE("Returning %08x %i\n", hr
, *frames
);
1410 static HRESULT WINAPI
ACC_ReleaseBuffer(IAudioCaptureClient
*iface
, UINT32 written
)
1412 ACCapture
*This
= (ACCapture
*)iface
;
1414 EnterCriticalSection(This
->parent
->crst
);
1415 if (!written
|| written
== This
->parent
->locked
) {
1416 This
->parent
->locked
= 0;
1417 This
->parent
->ofs_frames
+= written
;
1418 This
->parent
->ofs_frames
%= This
->parent
->bufsize_frames
;
1419 This
->parent
->pad
-= written
;
1420 } else if (!This
->parent
->locked
)
1421 hr
= AUDCLNT_E_OUT_OF_ORDER
;
1423 hr
= AUDCLNT_E_INVALID_SIZE
;
1424 LeaveCriticalSection(This
->parent
->crst
);
1428 static HRESULT WINAPI
ACC_GetNextPacketSize(IAudioCaptureClient
*iface
, UINT32
*frames
)
1430 ACCapture
*This
= (ACCapture
*)iface
;
1432 return AC_GetCurrentPadding((IAudioClient
*)This
->parent
, frames
);
1435 static const IAudioCaptureClientVtbl ACCapture_Vtbl
=
1442 ACC_GetNextPacketSize
1445 static HRESULT
AudioSessionControl_Create(ACImpl
*parent
, ACSession
**ppv
)
1448 This
= *ppv
= HeapAlloc(GetProcessHeap(), 0, sizeof(*This
));
1450 return E_OUTOFMEMORY
;
1451 This
->lpVtbl
= &ACSession_Vtbl
;
1453 This
->parent
= parent
;
1454 AC_AddRef((IAudioClient
*)This
->parent
);
1458 static void AudioSessionControl_Destroy(ACSession
*This
)
1460 This
->parent
->session
= NULL
;
1461 AC_Release((IAudioClient
*)This
->parent
);
1462 HeapFree(GetProcessHeap(), 0, This
);
1465 static HRESULT WINAPI
ACS_QueryInterface(IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
1467 TRACE("(%p)->(%s,%p)\n", iface
, debugstr_guid(riid
), ppv
);
1472 if (IsEqualIID(riid
, &IID_IUnknown
)
1473 || IsEqualIID(riid
, &IID_IAudioSessionControl
)
1474 || IsEqualIID(riid
, &IID_IAudioSessionControl2
))
1477 IUnknown_AddRef((IUnknown
*)*ppv
);
1480 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1481 return E_NOINTERFACE
;
1484 static ULONG WINAPI
ACS_AddRef(IAudioSessionControl2
*iface
)
1486 ACSession
*This
= (ACSession
*)iface
;
1488 ref
= InterlockedIncrement(&This
->ref
);
1489 TRACE("Refcount now %i\n", ref
);
1493 static ULONG WINAPI
ACS_Release(IAudioSessionControl2
*iface
)
1495 ACSession
*This
= (ACSession
*)iface
;
1497 ref
= InterlockedDecrement(&This
->ref
);
1498 TRACE("Refcount now %i\n", ref
);
1500 AudioSessionControl_Destroy(This
);
1504 static HRESULT WINAPI
ACS_GetState(IAudioSessionControl2
*iface
, AudioSessionState
*state
)
1506 ACSession
*This
= (ACSession
*)iface
;
1507 TRACE("(%p)->(%p)\n", This
, state
);
1511 *state
= This
->parent
->parent
->state
;
1515 static HRESULT WINAPI
ACS_GetDisplayName(IAudioSessionControl2
*iface
, WCHAR
**name
)
1517 ACSession
*This
= (ACSession
*)iface
;
1518 TRACE("(%p)->(%p)\n", This
, name
);
1525 static HRESULT WINAPI
ACS_SetDisplayName(IAudioSessionControl2
*iface
, const WCHAR
*name
, const GUID
*session
)
1527 ACSession
*This
= (ACSession
*)iface
;
1528 TRACE("(%p)->(%p,%s)\n", This
, name
, debugstr_guid(session
));
1533 static HRESULT WINAPI
ACS_GetIconPath(IAudioSessionControl2
*iface
, WCHAR
**path
)
1535 ACSession
*This
= (ACSession
*)iface
;
1536 TRACE("(%p)->(%p)\n", This
, path
);
1543 static HRESULT WINAPI
ACS_SetIconPath(IAudioSessionControl2
*iface
, const WCHAR
*path
, const GUID
*session
)
1545 ACSession
*This
= (ACSession
*)iface
;
1546 TRACE("(%p)->(%p,%s)\n", This
, path
, debugstr_guid(session
));
1551 static HRESULT WINAPI
ACS_GetGroupingParam(IAudioSessionControl2
*iface
, GUID
*group
)
1553 ACSession
*This
= (ACSession
*)iface
;
1554 TRACE("(%p)->(%p)\n", This
, group
);
1561 static HRESULT WINAPI
ACS_SetGroupingParam(IAudioSessionControl2
*iface
, GUID
*group
, const GUID
*session
)
1563 ACSession
*This
= (ACSession
*)iface
;
1564 TRACE("(%p)->(%s,%s)\n", This
, debugstr_guid(group
), debugstr_guid(session
));
1569 static HRESULT WINAPI
ACS_RegisterAudioSessionNotification(IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
1571 ACSession
*This
= (ACSession
*)iface
;
1572 TRACE("(%p)->(%p)\n", This
, events
);
1577 static HRESULT WINAPI
ACS_UnregisterAudioSessionNotification(IAudioSessionControl2
*iface
, IAudioSessionEvents
*events
)
1579 ACSession
*This
= (ACSession
*)iface
;
1580 TRACE("(%p)->(%p)\n", This
, events
);
1585 static HRESULT WINAPI
ACS_GetSessionIdentifier(IAudioSessionControl2
*iface
, WCHAR
**id
)
1587 ACSession
*This
= (ACSession
*)iface
;
1588 TRACE("(%p)->(%p)\n", This
, id
);
1595 static HRESULT WINAPI
ACS_GetSessionInstanceIdentifier(IAudioSessionControl2
*iface
, WCHAR
**id
)
1597 ACSession
*This
= (ACSession
*)iface
;
1598 TRACE("(%p)->(%p)\n", This
, id
);
1605 static HRESULT WINAPI
ACS_GetProcessId(IAudioSessionControl2
*iface
, DWORD
*pid
)
1607 ACSession
*This
= (ACSession
*)iface
;
1608 TRACE("(%p)->(%p)\n", This
, pid
);
1612 *pid
= GetCurrentProcessId();
1616 static HRESULT WINAPI
ACS_IsSystemSoundsSession(IAudioSessionControl2
*iface
)
1618 ACSession
*This
= (ACSession
*)iface
;
1619 TRACE("(%p)\n", This
);
1624 static HRESULT WINAPI
ACS_SetDuckingPreference(IAudioSessionControl2
*iface
, BOOL optout
)
1626 ACSession
*This
= (ACSession
*)iface
;
1627 TRACE("(%p)\n", This
);
1632 static const IAudioSessionControl2Vtbl ACSession_Vtbl
=
1642 ACS_GetGroupingParam
,
1643 ACS_SetGroupingParam
,
1644 ACS_RegisterAudioSessionNotification
,
1645 ACS_UnregisterAudioSessionNotification
,
1646 ACS_GetSessionIdentifier
,
1647 ACS_GetSessionInstanceIdentifier
,
1649 ACS_IsSystemSoundsSession
,
1650 ACS_SetDuckingPreference
1653 static HRESULT
AudioSimpleVolume_Create(ACImpl
*parent
, ASVolume
**ppv
)
1656 This
= *ppv
= HeapAlloc(GetProcessHeap(), 0, sizeof(*This
));
1658 return E_OUTOFMEMORY
;
1659 This
->lpVtbl
= &ASVolume_Vtbl
;
1661 This
->parent
= parent
;
1662 AC_AddRef((IAudioClient
*)This
->parent
);
1666 static void AudioSimpleVolume_Destroy(ASVolume
*This
)
1668 This
->parent
->svolume
= NULL
;
1669 AC_Release((IAudioClient
*)This
->parent
);
1670 HeapFree(GetProcessHeap(), 0, This
);
1673 static HRESULT WINAPI
ASV_QueryInterface(ISimpleAudioVolume
*iface
, REFIID riid
, void **ppv
)
1675 TRACE("(%p)->(%s,%p)\n", iface
, debugstr_guid(riid
), ppv
);
1680 if (IsEqualIID(riid
, &IID_IUnknown
)
1681 || IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
1684 IUnknown_AddRef((IUnknown
*)*ppv
);
1687 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1688 return E_NOINTERFACE
;
1691 static ULONG WINAPI
ASV_AddRef(ISimpleAudioVolume
*iface
)
1693 ASVolume
*This
= (ASVolume
*)iface
;
1695 ref
= InterlockedIncrement(&This
->ref
);
1696 TRACE("Refcount now %i\n", ref
);
1700 static ULONG WINAPI
ASV_Release(ISimpleAudioVolume
*iface
)
1702 ASVolume
*This
= (ASVolume
*)iface
;
1704 ref
= InterlockedDecrement(&This
->ref
);
1705 TRACE("Refcount now %i\n", ref
);
1707 AudioSimpleVolume_Destroy(This
);
1711 static HRESULT WINAPI
ASV_SetMasterVolume(ISimpleAudioVolume
*iface
, float level
, const GUID
*context
)
1713 ASVolume
*This
= (ASVolume
*)iface
;
1714 TRACE("(%p)->(%f,%p)\n", This
, level
, context
);
1720 static HRESULT WINAPI
ASV_GetMasterVolume(ISimpleAudioVolume
*iface
, float *level
)
1722 ASVolume
*This
= (ASVolume
*)iface
;
1723 TRACE("(%p)->(%p)\n", This
, level
);
1730 static HRESULT WINAPI
ASV_SetMute(ISimpleAudioVolume
*iface
, BOOL mute
, const GUID
*context
)
1732 ASVolume
*This
= (ASVolume
*)iface
;
1733 TRACE("(%p)->(%u,%p)\n", This
, mute
, context
);
1739 static HRESULT WINAPI
ASV_GetMute(ISimpleAudioVolume
*iface
, BOOL
*mute
)
1741 ASVolume
*This
= (ASVolume
*)iface
;
1742 TRACE("(%p)->(%p)\n", This
, mute
);
1749 static const ISimpleAudioVolumeVtbl ASVolume_Vtbl
=
1754 ASV_SetMasterVolume
,
1755 ASV_GetMasterVolume
,
1760 static HRESULT
AudioClock_Create(ACImpl
*parent
, AClock
**ppv
)
1763 This
= *ppv
= HeapAlloc(GetProcessHeap(), 0, sizeof(*This
));
1765 return E_OUTOFMEMORY
;
1766 This
->lpVtbl
= &AClock_Vtbl
;
1767 This
->lp2Vtbl
= &AClock2_Vtbl
;
1769 This
->parent
= parent
;
1770 AC_AddRef((IAudioClient
*)This
->parent
);
1774 static void AudioClock_Destroy(AClock
*This
)
1776 This
->parent
->clock
= NULL
;
1777 AC_Release((IAudioClient
*)This
->parent
);
1778 HeapFree(GetProcessHeap(), 0, This
);
1781 static HRESULT WINAPI
AClock_QueryInterface(IAudioClock
*iface
, REFIID riid
, void **ppv
)
1783 AClock
*This
= (AClock
*)iface
;
1784 TRACE("(%p)->(%s,%p)\n", iface
, debugstr_guid(riid
), ppv
);
1789 if (IsEqualIID(riid
, &IID_IUnknown
)
1790 || IsEqualIID(riid
, &IID_IAudioClock
))
1792 else if (IsEqualIID(riid
, &IID_IAudioClock2
))
1793 *ppv
= &This
->lp2Vtbl
;
1795 IUnknown_AddRef((IUnknown
*)*ppv
);
1798 WARN("Unknown interface %s\n", debugstr_guid(riid
));
1799 return E_NOINTERFACE
;
1802 static ULONG WINAPI
AClock_AddRef(IAudioClock
*iface
)
1804 AClock
*This
= (AClock
*)iface
;
1806 ref
= InterlockedIncrement(&This
->ref
);
1807 TRACE("Refcount now %i\n", ref
);
1811 static ULONG WINAPI
AClock_Release(IAudioClock
*iface
)
1813 AClock
*This
= (AClock
*)iface
;
1815 ref
= InterlockedDecrement(&This
->ref
);
1816 TRACE("Refcount now %i\n", ref
);
1818 AudioClock_Destroy(This
);
1822 static HRESULT WINAPI
AClock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
1824 AClock
*This
= (AClock
*)iface
;
1825 TRACE("(%p)->(%p)\n", This
, freq
);
1827 *freq
= (UINT64
)This
->parent
->pwfx
->nSamplesPerSec
;
1831 static HRESULT WINAPI
AClock_GetPosition(IAudioClock
*iface
, UINT64
*pos
, UINT64
*qpctime
)
1833 AClock
*This
= (AClock
*)iface
;
1836 TRACE("(%p)->(%p,%p)\n", This
, pos
, qpctime
);
1841 EnterCriticalSection(This
->parent
->crst
);
1842 AC_GetCurrentPadding((IAudioClient
*)This
->parent
, &pad
);
1843 *pos
= This
->parent
->frameswritten
- pad
;
1845 *qpctime
= gettime();
1846 LeaveCriticalSection(This
->parent
->crst
);
1851 static HRESULT WINAPI
AClock_GetCharacteristics(IAudioClock
*iface
, DWORD
*chars
)
1853 AClock
*This
= (AClock
*)iface
;
1854 TRACE("(%p)->(%p)\n", This
, chars
);
1858 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
1862 static const IAudioClockVtbl AClock_Vtbl
=
1864 AClock_QueryInterface
,
1867 AClock_GetFrequency
,
1869 AClock_GetCharacteristics
1872 static AClock
*get_clock_from_clock2(IAudioClock2
*iface
)
1874 return (AClock
*)((char*)iface
- offsetof(AClock
,lp2Vtbl
));
1877 static HRESULT WINAPI
AClock2_QueryInterface(IAudioClock2
*iface
, REFIID riid
, void **ppv
)
1879 AClock
*This
= get_clock_from_clock2(iface
);
1880 return IUnknown_QueryInterface((IUnknown
*)This
, riid
, ppv
);
1883 static ULONG WINAPI
AClock2_AddRef(IAudioClock2
*iface
)
1885 AClock
*This
= get_clock_from_clock2(iface
);
1886 return IUnknown_AddRef((IUnknown
*)This
);
1889 static ULONG WINAPI
AClock2_Release(IAudioClock2
*iface
)
1891 AClock
*This
= get_clock_from_clock2(iface
);
1892 return IUnknown_Release((IUnknown
*)This
);
1895 static HRESULT WINAPI
AClock2_GetPosition(IAudioClock2
*iface
, UINT64
*pos
, UINT64
*qpctime
)
1897 AClock
*This
= get_clock_from_clock2(iface
);
1898 return AClock_GetPosition((IAudioClock
*)This
, pos
, qpctime
);
1901 static const IAudioClock2Vtbl AClock2_Vtbl
=
1903 AClock2_QueryInterface
,