uxtheme: Remove unused variable.
[wine] / dlls / winealsa.drv / mmdevdrv.c
1 /*
2  * Copyright 2010 Maarten Lankhorst for CodeWeavers
3  * Copyright 2011 Andrew Eikum for CodeWeavers
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #define NONAMELESSUNION
21 #define COBJMACROS
22 #include "config.h"
23
24 #include <stdarg.h>
25 #include <math.h>
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winnls.h"
30 #include "winreg.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33 #include "wine/list.h"
34
35 #include "ole2.h"
36 #include "mmdeviceapi.h"
37 #include "devpkey.h"
38 #include "mmsystem.h"
39 #include "dsound.h"
40
41 #include "initguid.h"
42 #include "endpointvolume.h"
43 #include "audioclient.h"
44 #include "audiopolicy.h"
45
46 #include <alsa/asoundlib.h>
47
48 WINE_DEFAULT_DEBUG_CHANNEL(alsa);
49 WINE_DECLARE_DEBUG_CHANNEL(winediag);
50
51 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
52
53 static const REFERENCE_TIME DefaultPeriod = 100000;
54 static const REFERENCE_TIME MinimumPeriod = 50000;
55 #define                     EXTRA_SAFE_RT   40000
56
57 struct ACImpl;
58 typedef struct ACImpl ACImpl;
59
60 typedef struct _AudioSession {
61     GUID guid;
62     struct list clients;
63
64     IMMDevice *device;
65
66     float master_vol;
67     UINT32 channel_count;
68     float *channel_vols;
69     BOOL mute;
70
71     CRITICAL_SECTION lock;
72
73     struct list entry;
74 } AudioSession;
75
76 typedef struct _AudioSessionWrapper {
77     IAudioSessionControl2 IAudioSessionControl2_iface;
78     IChannelAudioVolume IChannelAudioVolume_iface;
79     ISimpleAudioVolume ISimpleAudioVolume_iface;
80
81     LONG ref;
82
83     ACImpl *client;
84     AudioSession *session;
85 } AudioSessionWrapper;
86
87 struct ACImpl {
88     IAudioClient IAudioClient_iface;
89     IAudioRenderClient IAudioRenderClient_iface;
90     IAudioCaptureClient IAudioCaptureClient_iface;
91     IAudioClock IAudioClock_iface;
92     IAudioClock2 IAudioClock2_iface;
93     IAudioStreamVolume IAudioStreamVolume_iface;
94
95     LONG ref;
96
97     snd_pcm_t *pcm_handle;
98     snd_pcm_uframes_t alsa_bufsize_frames, alsa_period_frames;
99     snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
100     snd_pcm_format_t alsa_format;
101
102     IMMDevice *parent;
103
104     EDataFlow dataflow;
105     WAVEFORMATEX *fmt;
106     DWORD flags;
107     AUDCLNT_SHAREMODE share;
108     HANDLE event;
109     float *vols;
110
111     BOOL need_remapping;
112     int alsa_channels;
113     int alsa_channel_map[32];
114
115     BOOL initted, started;
116     REFERENCE_TIME mmdev_period_rt;
117     UINT64 written_frames, last_pos_frames;
118     UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
119     snd_pcm_uframes_t remapping_buf_frames;
120     UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
121     UINT32 hidden_frames;   /* ALSA reserve to ensure continuous rendering */
122
123     HANDLE timer;
124     BYTE *local_buffer, *tmp_buffer, *remapping_buf;
125     LONG32 getbuf_last; /* <0 when using tmp_buffer */
126
127     CRITICAL_SECTION lock;
128
129     AudioSession *session;
130     AudioSessionWrapper *session_wrapper;
131
132     struct list entry;
133 };
134
135 typedef struct _SessionMgr {
136     IAudioSessionManager2 IAudioSessionManager2_iface;
137
138     LONG ref;
139
140     IMMDevice *device;
141 } SessionMgr;
142
143 static HANDLE g_timer_q;
144
145 static CRITICAL_SECTION g_sessions_lock;
146 static CRITICAL_SECTION_DEBUG g_sessions_lock_debug =
147 {
148     0, 0, &g_sessions_lock,
149     { &g_sessions_lock_debug.ProcessLocksList, &g_sessions_lock_debug.ProcessLocksList },
150       0, 0, { (DWORD_PTR)(__FILE__ ": g_sessions_lock") }
151 };
152 static CRITICAL_SECTION g_sessions_lock = { &g_sessions_lock_debug, -1, 0, 0, 0, 0 };
153 static struct list g_sessions = LIST_INIT(g_sessions);
154
155 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0};
156 static const char defname[] = "default";
157
158 static const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\',
159     'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
160     'w','i','n','e','a','l','s','a','.','d','r','v',0};
161 static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
162     'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
163     'w','i','n','e','a','l','s','a','.','d','r','v','\\','d','e','v','i','c','e','s',0};
164 static const WCHAR guidW[] = {'g','u','i','d',0};
165
166 static const IAudioClientVtbl AudioClient_Vtbl;
167 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
168 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
169 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
170 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
171 static const IAudioClockVtbl AudioClock_Vtbl;
172 static const IAudioClock2Vtbl AudioClock2_Vtbl;
173 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
174 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
175 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl;
176
177 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
178
179 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
180 {
181     return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
182 }
183
184 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
185 {
186     return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
187 }
188
189 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
190 {
191     return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
192 }
193
194 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
195 {
196     return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
197 }
198
199 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
200 {
201     return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
202 }
203
204 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
205 {
206     return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
207 }
208
209 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
210 {
211     return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
212 }
213
214 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
215 {
216     return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
217 }
218
219 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
220 {
221     return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
222 }
223
224 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
225 {
226     return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
227 }
228
229 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
230 {
231     switch (reason)
232     {
233     case DLL_PROCESS_ATTACH:
234         g_timer_q = CreateTimerQueue();
235         if(!g_timer_q)
236             return FALSE;
237         break;
238
239     case DLL_PROCESS_DETACH:
240         DeleteCriticalSection(&g_sessions_lock);
241         break;
242     }
243     return TRUE;
244 }
245
246 /* From <dlls/mmdevapi/mmdevapi.h> */
247 enum DriverPriority {
248     Priority_Unavailable = 0,
249     Priority_Low,
250     Priority_Neutral,
251     Priority_Preferred
252 };
253
254 int WINAPI AUDDRV_GetPriority(void)
255 {
256     return Priority_Neutral;
257 }
258
259 static void set_device_guid(EDataFlow flow, HKEY drv_key, const WCHAR *key_name,
260         GUID *guid)
261 {
262     HKEY key;
263     BOOL opened = FALSE;
264     LONG lr;
265
266     if(!drv_key){
267         lr = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0, KEY_WRITE,
268                     NULL, &drv_key, NULL);
269         if(lr != ERROR_SUCCESS){
270             ERR("RegCreateKeyEx(drv_key) failed: %u\n", lr);
271             return;
272         }
273         opened = TRUE;
274     }
275
276     lr = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_WRITE,
277                 NULL, &key, NULL);
278     if(lr != ERROR_SUCCESS){
279         ERR("RegCreateKeyEx(%s) failed: %u\n", wine_dbgstr_w(key_name), lr);
280         goto exit;
281     }
282
283     lr = RegSetValueExW(key, guidW, 0, REG_BINARY, (BYTE*)guid,
284                 sizeof(GUID));
285     if(lr != ERROR_SUCCESS)
286         ERR("RegSetValueEx(%s\\guid) failed: %u\n", wine_dbgstr_w(key_name), lr);
287
288     RegCloseKey(key);
289 exit:
290     if(opened)
291         RegCloseKey(drv_key);
292 }
293
294 static void get_device_guid(EDataFlow flow, const char *device, GUID *guid)
295 {
296     HKEY key = NULL, dev_key;
297     DWORD type, size = sizeof(*guid);
298     WCHAR key_name[256];
299
300     if(flow == eCapture)
301         key_name[0] = '1';
302     else
303         key_name[0] = '0';
304     key_name[1] = ',';
305     MultiByteToWideChar(CP_UNIXCP, 0, device, -1, key_name + 2,
306             (sizeof(key_name) / sizeof(*key_name)) - 2);
307
308     if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){
309         if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){
310             if(RegQueryValueExW(dev_key, guidW, 0, &type,
311                         (BYTE*)guid, &size) == ERROR_SUCCESS){
312                 if(type == REG_BINARY){
313                     RegCloseKey(dev_key);
314                     RegCloseKey(key);
315                     return;
316                 }
317                 ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
318                         wine_dbgstr_w(key_name), type);
319             }
320             RegCloseKey(dev_key);
321         }
322     }
323
324     CoCreateGuid(guid);
325
326     set_device_guid(flow, key, key_name, guid);
327
328     if(key)
329         RegCloseKey(key);
330 }
331
332 static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
333 {
334     snd_pcm_t *handle;
335     int err;
336
337     TRACE("devnode: %s, stream: %d\n", devnode, stream);
338
339     if((err = snd_pcm_open(&handle, devnode, stream, SND_PCM_NONBLOCK)) < 0){
340         WARN("The device \"%s\" failed to open: %d (%s).\n",
341                 devnode, err, snd_strerror(err));
342         return FALSE;
343     }
344
345     snd_pcm_close(handle);
346     return TRUE;
347 }
348
349 static WCHAR *construct_device_id(EDataFlow flow, const WCHAR *chunk1, const char *chunk2)
350 {
351     WCHAR *ret;
352     const WCHAR *prefix;
353     DWORD len_wchars = 0, chunk1_len, copied = 0, prefix_len;
354
355     static const WCHAR dashW[] = {' ','-',' ',0};
356     static const size_t dashW_len = (sizeof(dashW) / sizeof(*dashW)) - 1;
357     static const WCHAR outW[] = {'O','u','t',':',' ',0};
358     static const WCHAR inW[] = {'I','n',':',' ',0};
359
360     if(flow == eRender){
361         prefix = outW;
362         prefix_len = (sizeof(outW) / sizeof(*outW)) - 1;
363         len_wchars += prefix_len;
364     }else{
365         prefix = inW;
366         prefix_len = (sizeof(inW) / sizeof(*inW)) - 1;
367         len_wchars += prefix_len;
368     }
369     if(chunk1){
370         chunk1_len = strlenW(chunk1);
371         len_wchars += chunk1_len;
372     }
373     if(chunk1 && chunk2)
374         len_wchars += dashW_len;
375     if(chunk2)
376         len_wchars += MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, NULL, 0) - 1;
377     len_wchars += 1; /* NULL byte */
378
379     ret = HeapAlloc(GetProcessHeap(), 0, len_wchars * sizeof(WCHAR));
380
381     memcpy(ret, prefix, prefix_len * sizeof(WCHAR));
382     copied += prefix_len;
383     if(chunk1){
384         memcpy(ret + copied, chunk1, chunk1_len * sizeof(WCHAR));
385         copied += chunk1_len;
386     }
387     if(chunk1 && chunk2){
388         memcpy(ret + copied, dashW, dashW_len * sizeof(WCHAR));
389         copied += dashW_len;
390     }
391     if(chunk2){
392         MultiByteToWideChar(CP_UNIXCP, 0, chunk2, -1, ret + copied, len_wchars - copied);
393     }else
394         ret[copied] = 0;
395
396     TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret));
397
398     return ret;
399 }
400
401 static HRESULT alsa_get_card_devices(EDataFlow flow, snd_pcm_stream_t stream,
402         WCHAR ***ids, GUID **guids, UINT *num, snd_ctl_t *ctl, int card,
403         const WCHAR *cardnameW)
404 {
405     int err, device;
406     snd_pcm_info_t *info;
407
408     info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_info_sizeof());
409     if(!info)
410         return E_OUTOFMEMORY;
411
412     snd_pcm_info_set_subdevice(info, 0);
413     snd_pcm_info_set_stream(info, stream);
414
415     device = -1;
416     for(err = snd_ctl_pcm_next_device(ctl, &device); device != -1 && err >= 0;
417             err = snd_ctl_pcm_next_device(ctl, &device)){
418         const char *devname;
419         char devnode[32];
420
421         snd_pcm_info_set_device(info, device);
422
423         if((err = snd_ctl_pcm_info(ctl, info)) < 0){
424             if(err == -ENOENT)
425                 /* This device doesn't have the right stream direction */
426                 continue;
427
428             WARN("Failed to get info for card %d, device %d: %d (%s)\n",
429                     card, device, err, snd_strerror(err));
430             continue;
431         }
432
433         sprintf(devnode, "plughw:%d,%d", card, device);
434         if(!alsa_try_open(devnode, stream))
435             continue;
436
437         if(*num){
438             *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
439             *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
440         }else{
441             *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
442             *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
443         }
444
445         devname = snd_pcm_info_get_name(info);
446         if(!devname){
447             WARN("Unable to get device name for card %d, device %d\n", card,
448                     device);
449             continue;
450         }
451
452         (*ids)[*num] = construct_device_id(flow, cardnameW, devname);
453         get_device_guid(flow, devnode, &(*guids)[*num]);
454
455         ++(*num);
456     }
457
458     HeapFree(GetProcessHeap(), 0, info);
459
460     if(err != 0)
461         WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
462                 card, err, snd_strerror(err));
463
464     return S_OK;
465 }
466
467 static void get_reg_devices(EDataFlow flow, snd_pcm_stream_t stream, WCHAR ***ids,
468         GUID **guids, UINT *num)
469 {
470     static const WCHAR ALSAOutputDevices[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
471     static const WCHAR ALSAInputDevices[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
472     HKEY key;
473     WCHAR reg_devices[256];
474     DWORD size = sizeof(reg_devices), type;
475     const WCHAR *value_name = (stream == SND_PCM_STREAM_PLAYBACK) ? ALSAOutputDevices : ALSAInputDevices;
476
477     /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
478     if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
479         if(RegQueryValueExW(key, value_name, 0, &type,
480                     (BYTE*)reg_devices, &size) == ERROR_SUCCESS){
481             WCHAR *p = reg_devices;
482
483             if(type != REG_MULTI_SZ){
484                 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
485                 RegCloseKey(key);
486                 return;
487             }
488
489             while(*p){
490                 char devname[64];
491
492                 WideCharToMultiByte(CP_UNIXCP, 0, p, -1, devname, sizeof(devname), NULL, NULL);
493
494                 if(alsa_try_open(devname, stream)){
495                     if(*num){
496                         *ids = HeapReAlloc(GetProcessHeap(), 0, *ids, sizeof(WCHAR *) * (*num + 1));
497                         *guids = HeapReAlloc(GetProcessHeap(), 0, *guids, sizeof(GUID) * (*num + 1));
498                     }else{
499                         *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
500                         *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
501                     }
502                     (*ids)[*num] = construct_device_id(flow, p, NULL);
503                     get_device_guid(flow, devname, &(*guids)[*num]);
504                     ++*num;
505                 }
506
507                 p += lstrlenW(p) + 1;
508             }
509         }
510
511         RegCloseKey(key);
512     }
513 }
514
515 static HRESULT alsa_enum_devices(EDataFlow flow, WCHAR ***ids, GUID **guids,
516         UINT *num)
517 {
518     snd_pcm_stream_t stream = (flow == eRender ? SND_PCM_STREAM_PLAYBACK :
519         SND_PCM_STREAM_CAPTURE);
520     int err, card;
521
522     card = -1;
523     *num = 0;
524
525     if(alsa_try_open(defname, stream)){
526         *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
527         (*ids)[0] = construct_device_id(flow, defaultW, NULL);
528         *guids = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
529         get_device_guid(flow, defname, &(*guids)[0]);
530         ++*num;
531     }
532
533     get_reg_devices(flow, stream, ids, guids, num);
534
535     for(err = snd_card_next(&card); card != -1 && err >= 0;
536             err = snd_card_next(&card)){
537         char cardpath[64];
538         char *cardname;
539         WCHAR *cardnameW;
540         snd_ctl_t *ctl;
541         DWORD len;
542
543         sprintf(cardpath, "hw:%u", card);
544
545         if((err = snd_ctl_open(&ctl, cardpath, 0)) < 0){
546             WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath,
547                     err, snd_strerror(err));
548             continue;
549         }
550
551         if(snd_card_get_name(card, &cardname) < 0) {
552             /* FIXME: Should be localized */
553             static const WCHAR nameW[] = {'U','n','k','n','o','w','n',' ','s','o','u','n','d','c','a','r','d',0};
554             WARN("Unable to get card name for ALSA device %s: %d (%s)\n",
555                     cardpath, err, snd_strerror(err));
556             alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, nameW);
557         }else{
558             len = MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, NULL, 0);
559             cardnameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
560
561             if(!cardnameW){
562                 free(cardname);
563                 snd_ctl_close(ctl);
564                 return E_OUTOFMEMORY;
565             }
566             MultiByteToWideChar(CP_UNIXCP, 0, cardname, -1, cardnameW, len);
567
568             alsa_get_card_devices(flow, stream, ids, guids, num, ctl, card, cardnameW);
569
570             HeapFree(GetProcessHeap(), 0, cardnameW);
571             free(cardname);
572         }
573
574         snd_ctl_close(ctl);
575     }
576
577     if(err != 0)
578         WARN("Got a failure during card enumeration: %d (%s)\n",
579                 err, snd_strerror(err));
580
581     return S_OK;
582 }
583
584 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **guids,
585         UINT *num, UINT *def_index)
586 {
587     HRESULT hr;
588
589     TRACE("%d %p %p %p %p\n", flow, ids, guids, num, def_index);
590
591     *ids = NULL;
592     *guids = NULL;
593
594     hr = alsa_enum_devices(flow, ids, guids, num);
595     if(FAILED(hr)){
596         UINT i;
597         for(i = 0; i < *num; ++i)
598             HeapFree(GetProcessHeap(), 0, (*ids)[i]);
599         HeapFree(GetProcessHeap(), 0, *ids);
600         HeapFree(GetProcessHeap(), 0, *guids);
601         return E_OUTOFMEMORY;
602     }
603
604     TRACE("Enumerated %u devices\n", *num);
605
606     if(*num == 0){
607         HeapFree(GetProcessHeap(), 0, *ids);
608         *ids = NULL;
609         HeapFree(GetProcessHeap(), 0, *guids);
610         *guids = NULL;
611     }
612
613     *def_index = 0;
614
615     return S_OK;
616 }
617
618 /* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
619  * which causes audio to cease playing after a few minutes of playback.
620  * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
621  * around this issue. */
622 static snd_config_t *make_handle_underrun_config(const char *name)
623 {
624     snd_config_t *lconf, *dev_node, *hu_node, *type_node;
625     char dev_node_name[64];
626     const char *type_str;
627     int err;
628
629     snd_config_update();
630
631     if((err = snd_config_copy(&lconf, snd_config)) < 0){
632         WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
633         return NULL;
634     }
635
636     sprintf(dev_node_name, "pcm.%s", name);
637     err = snd_config_search(lconf, dev_node_name, &dev_node);
638     if(err == -ENOENT){
639         snd_config_delete(lconf);
640         return NULL;
641     }
642     if(err < 0){
643         snd_config_delete(lconf);
644         WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
645         return NULL;
646     }
647
648     /* ALSA is extremely fragile. If it runs into a config setting it doesn't
649      * recognize, it tends to fail or assert. So we only want to inject
650      * handle_underrun=1 on devices that we know will recognize it. */
651     err = snd_config_search(dev_node, "type", &type_node);
652     if(err == -ENOENT){
653         snd_config_delete(lconf);
654         return NULL;
655     }
656     if(err < 0){
657         snd_config_delete(lconf);
658         WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
659         return NULL;
660     }
661
662     if((err = snd_config_get_string(type_node, &type_str)) < 0){
663         snd_config_delete(lconf);
664         return NULL;
665     }
666
667     if(strcmp(type_str, "pulse") != 0){
668         snd_config_delete(lconf);
669         return NULL;
670     }
671
672     err = snd_config_search(dev_node, "handle_underrun", &hu_node);
673     if(err >= 0){
674         /* user already has an explicit handle_underrun setting, so don't
675          * use a local config */
676         snd_config_delete(lconf);
677         return NULL;
678     }
679     if(err != -ENOENT){
680         snd_config_delete(lconf);
681         WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
682         return NULL;
683     }
684
685     if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
686         snd_config_delete(lconf);
687         WARN("snd_config_imake_integer failed: %d (%s)\n", err,
688                 snd_strerror(err));
689         return NULL;
690     }
691
692     if((err = snd_config_add(dev_node, hu_node)) < 0){
693         snd_config_delete(lconf);
694         WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
695         return NULL;
696     }
697
698     return lconf;
699 }
700
701 static BOOL get_alsa_name_by_guid(GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
702 {
703     HKEY devices_key;
704     UINT i = 0;
705     WCHAR key_name[256];
706     DWORD key_name_size;
707
708     if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ, &devices_key) != ERROR_SUCCESS){
709         ERR("No devices found in registry?\n");
710         return FALSE;
711     }
712
713     while(1){
714         HKEY key;
715         DWORD size, type;
716         GUID reg_guid;
717
718         key_name_size = sizeof(key_name)/sizeof(WCHAR);
719         if(RegEnumKeyExW(devices_key, i, key_name, &key_name_size, NULL,
720                 NULL, NULL, NULL) != ERROR_SUCCESS)
721             break;
722
723         if(RegOpenKeyExW(devices_key, key_name, 0, KEY_READ, &key) != ERROR_SUCCESS){
724             WARN("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
725             continue;
726         }
727
728         size = sizeof(reg_guid);
729         if(RegQueryValueExW(key, guidW, 0, &type,
730                     (BYTE*)&reg_guid, &size) == ERROR_SUCCESS){
731             if(IsEqualGUID(&reg_guid, guid)){
732                 RegCloseKey(key);
733                 RegCloseKey(devices_key);
734
735                 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
736
737                 if(key_name[0] == '0')
738                     *flow = eRender;
739                 else if(key_name[0] == '1')
740                     *flow = eCapture;
741                 else{
742                     ERR("Unknown device type: %c\n", key_name[0]);
743                     return FALSE;
744                 }
745
746                 WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
747
748                 return TRUE;
749             }
750         }
751
752         RegCloseKey(key);
753
754         ++i;
755     }
756
757     RegCloseKey(devices_key);
758
759     WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
760
761     return FALSE;
762 }
763
764 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
765 {
766     ACImpl *This;
767     int err;
768     snd_pcm_stream_t stream;
769     snd_config_t *lconf;
770     static int handle_underrun = 1;
771     char alsa_name[256];
772     EDataFlow dataflow;
773
774     TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
775
776     if(!get_alsa_name_by_guid(guid, alsa_name, sizeof(alsa_name), &dataflow))
777         return AUDCLNT_E_DEVICE_INVALIDATED;
778
779     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
780     if(!This)
781         return E_OUTOFMEMORY;
782
783     This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
784     This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
785     This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
786     This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
787     This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
788     This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
789
790     if(dataflow == eRender)
791         stream = SND_PCM_STREAM_PLAYBACK;
792     else if(dataflow == eCapture)
793         stream = SND_PCM_STREAM_CAPTURE;
794     else{
795         HeapFree(GetProcessHeap(), 0, This);
796         return E_UNEXPECTED;
797     }
798
799     This->dataflow = dataflow;
800     if(handle_underrun && ((lconf = make_handle_underrun_config(alsa_name)))){
801         err = snd_pcm_open_lconf(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK, lconf);
802         TRACE("Opening PCM device \"%s\" with handle_underrun: %d\n", alsa_name, err);
803         snd_config_delete(lconf);
804         /* Pulse <= 2010 returns EINVAL, it does not know handle_underrun. */
805         if(err == -EINVAL){
806             ERR_(winediag)("PulseAudio \"%s\" %d without handle_underrun. Audio may hang."
807                            " Please upgrade to alsa_plugins >= 1.0.24\n", alsa_name, err);
808             handle_underrun = 0;
809         }
810     }else
811         err = -EINVAL;
812     if(err == -EINVAL){
813         err = snd_pcm_open(&This->pcm_handle, alsa_name, stream, SND_PCM_NONBLOCK);
814     }
815     if(err < 0){
816         HeapFree(GetProcessHeap(), 0, This);
817         WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
818         switch(err){
819         case -EBUSY:
820             return AUDCLNT_E_DEVICE_IN_USE;
821         default:
822             return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
823         }
824     }
825
826     This->hw_params = HeapAlloc(GetProcessHeap(), 0,
827             snd_pcm_hw_params_sizeof());
828     if(!This->hw_params){
829         snd_pcm_close(This->pcm_handle);
830         HeapFree(GetProcessHeap(), 0, This);
831         return E_OUTOFMEMORY;
832     }
833
834     InitializeCriticalSection(&This->lock);
835     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ACImpl.lock");
836
837     This->parent = dev;
838     IMMDevice_AddRef(This->parent);
839
840     *out = &This->IAudioClient_iface;
841     IAudioClient_AddRef(&This->IAudioClient_iface);
842
843     return S_OK;
844 }
845
846 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
847         REFIID riid, void **ppv)
848 {
849     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
850
851     if(!ppv)
852         return E_POINTER;
853     *ppv = NULL;
854     if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
855         *ppv = iface;
856     if(*ppv){
857         IUnknown_AddRef((IUnknown*)*ppv);
858         return S_OK;
859     }
860     WARN("Unknown interface %s\n", debugstr_guid(riid));
861     return E_NOINTERFACE;
862 }
863
864 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
865 {
866     ACImpl *This = impl_from_IAudioClient(iface);
867     ULONG ref;
868     ref = InterlockedIncrement(&This->ref);
869     TRACE("(%p) Refcount now %u\n", This, ref);
870     return ref;
871 }
872
873 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
874 {
875     ACImpl *This = impl_from_IAudioClient(iface);
876     ULONG ref;
877     ref = InterlockedDecrement(&This->ref);
878     TRACE("(%p) Refcount now %u\n", This, ref);
879     if(!ref){
880         IAudioClient_Stop(iface);
881         IMMDevice_Release(This->parent);
882         This->lock.DebugInfo->Spare[0] = 0;
883         DeleteCriticalSection(&This->lock);
884         snd_pcm_drop(This->pcm_handle);
885         snd_pcm_close(This->pcm_handle);
886         if(This->initted){
887             EnterCriticalSection(&g_sessions_lock);
888             list_remove(&This->entry);
889             LeaveCriticalSection(&g_sessions_lock);
890         }
891         HeapFree(GetProcessHeap(), 0, This->vols);
892         HeapFree(GetProcessHeap(), 0, This->local_buffer);
893         HeapFree(GetProcessHeap(), 0, This->remapping_buf);
894         HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
895         HeapFree(GetProcessHeap(), 0, This->hw_params);
896         CoTaskMemFree(This->fmt);
897         HeapFree(GetProcessHeap(), 0, This);
898     }
899     return ref;
900 }
901
902 static void dump_fmt(const WAVEFORMATEX *fmt)
903 {
904     TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
905     switch(fmt->wFormatTag){
906     case WAVE_FORMAT_PCM:
907         TRACE("WAVE_FORMAT_PCM");
908         break;
909     case WAVE_FORMAT_IEEE_FLOAT:
910         TRACE("WAVE_FORMAT_IEEE_FLOAT");
911         break;
912     case WAVE_FORMAT_EXTENSIBLE:
913         TRACE("WAVE_FORMAT_EXTENSIBLE");
914         break;
915     default:
916         TRACE("Unknown");
917         break;
918     }
919     TRACE(")\n");
920
921     TRACE("nChannels: %u\n", fmt->nChannels);
922     TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
923     TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
924     TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
925     TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
926     TRACE("cbSize: %u\n", fmt->cbSize);
927
928     if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
929         WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
930         TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
931         TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
932         TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
933     }
934 }
935
936 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
937 {
938     WAVEFORMATEX *ret;
939     size_t size;
940
941     if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
942         size = sizeof(WAVEFORMATEXTENSIBLE);
943     else
944         size = sizeof(WAVEFORMATEX);
945
946     ret = CoTaskMemAlloc(size);
947     if(!ret)
948         return NULL;
949
950     memcpy(ret, fmt, size);
951
952     ret->cbSize = size - sizeof(WAVEFORMATEX);
953
954     return ret;
955 }
956
957 static snd_pcm_format_t alsa_format(const WAVEFORMATEX *fmt)
958 {
959     snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
960     const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
961
962     if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
963       (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
964        IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
965         if(fmt->wBitsPerSample == 8)
966             format = SND_PCM_FORMAT_U8;
967         else if(fmt->wBitsPerSample == 16)
968             format = SND_PCM_FORMAT_S16_LE;
969         else if(fmt->wBitsPerSample == 24)
970             format = SND_PCM_FORMAT_S24_3LE;
971         else if(fmt->wBitsPerSample == 32)
972             format = SND_PCM_FORMAT_S32_LE;
973         else
974             WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
975         if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
976            fmt->wBitsPerSample != fmtex->Samples.wValidBitsPerSample){
977             if(fmtex->Samples.wValidBitsPerSample == 20 && fmt->wBitsPerSample == 24)
978                 format = SND_PCM_FORMAT_S20_3LE;
979             else{
980                 WARN("Unsupported ValidBits: %u\n", fmtex->Samples.wValidBitsPerSample);
981                 format = SND_PCM_FORMAT_UNKNOWN;
982             }
983         }
984     }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
985             (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
986              IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
987         if(fmt->wBitsPerSample == 32)
988             format = SND_PCM_FORMAT_FLOAT_LE;
989         else if(fmt->wBitsPerSample == 64)
990             format = SND_PCM_FORMAT_FLOAT64_LE;
991         else
992             WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
993     }else
994         WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
995     return format;
996 }
997
998 static void session_init_vols(AudioSession *session, UINT channels)
999 {
1000     if(session->channel_count < channels){
1001         UINT i;
1002
1003         if(session->channel_vols)
1004             session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
1005                     session->channel_vols, sizeof(float) * channels);
1006         else
1007             session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
1008                     sizeof(float) * channels);
1009         if(!session->channel_vols)
1010             return;
1011
1012         for(i = session->channel_count; i < channels; ++i)
1013             session->channel_vols[i] = 1.f;
1014
1015         session->channel_count = channels;
1016     }
1017 }
1018
1019 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
1020         UINT num_channels)
1021 {
1022     AudioSession *ret;
1023
1024     ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
1025     if(!ret)
1026         return NULL;
1027
1028     memcpy(&ret->guid, guid, sizeof(GUID));
1029
1030     ret->device = device;
1031
1032     list_init(&ret->clients);
1033
1034     list_add_head(&g_sessions, &ret->entry);
1035
1036     InitializeCriticalSection(&ret->lock);
1037     ret->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AudioSession.lock");
1038
1039     session_init_vols(ret, num_channels);
1040
1041     ret->master_vol = 1.f;
1042
1043     return ret;
1044 }
1045
1046 /* if channels == 0, then this will return or create a session with
1047  * matching dataflow and GUID. otherwise, channels must also match */
1048 static HRESULT get_audio_session(const GUID *sessionguid,
1049         IMMDevice *device, UINT channels, AudioSession **out)
1050 {
1051     AudioSession *session;
1052
1053     if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
1054         *out = create_session(&GUID_NULL, device, channels);
1055         if(!*out)
1056             return E_OUTOFMEMORY;
1057
1058         return S_OK;
1059     }
1060
1061     *out = NULL;
1062     LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
1063         if(session->device == device &&
1064                 IsEqualGUID(sessionguid, &session->guid)){
1065             session_init_vols(session, channels);
1066             *out = session;
1067             break;
1068         }
1069     }
1070
1071     if(!*out){
1072         *out = create_session(sessionguid, device, channels);
1073         if(!*out)
1074             return E_OUTOFMEMORY;
1075     }
1076
1077     return S_OK;
1078 }
1079
1080 static int alsa_channel_index(DWORD flag)
1081 {
1082     switch(flag){
1083     case SPEAKER_FRONT_LEFT:
1084         return 0;
1085     case SPEAKER_FRONT_RIGHT:
1086         return 1;
1087     case SPEAKER_BACK_LEFT:
1088         return 2;
1089     case SPEAKER_BACK_RIGHT:
1090         return 3;
1091     case SPEAKER_FRONT_CENTER:
1092         return 4;
1093     case SPEAKER_LOW_FREQUENCY:
1094         return 5;
1095     case SPEAKER_SIDE_LEFT:
1096         return 6;
1097     case SPEAKER_SIDE_RIGHT:
1098         return 7;
1099     }
1100     return -1;
1101 }
1102
1103 static BOOL need_remapping(ACImpl *This, const WAVEFORMATEX *fmt)
1104 {
1105     unsigned int i;
1106     for(i = 0; i < fmt->nChannels; ++i){
1107         if(This->alsa_channel_map[i] != i)
1108             return TRUE;
1109     }
1110     return FALSE;
1111 }
1112
1113 static DWORD get_channel_mask(unsigned int channels)
1114 {
1115     switch(channels){
1116     case 0:
1117         return 0;
1118     case 1:
1119         return KSAUDIO_SPEAKER_MONO;
1120     case 2:
1121         return KSAUDIO_SPEAKER_STEREO;
1122     case 3:
1123         return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
1124     case 4:
1125         return KSAUDIO_SPEAKER_QUAD;    /* not _SURROUND */
1126     case 5:
1127         return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
1128     case 6:
1129         return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
1130     case 7:
1131         return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
1132     case 8:
1133         return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
1134     }
1135     FIXME("Unknown speaker configuration: %u\n", channels);
1136     return 0;
1137 }
1138
1139 static HRESULT map_channels(ACImpl *This, const WAVEFORMATEX *fmt)
1140 {
1141     if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE || fmt->nChannels > 2){
1142         WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
1143         DWORD mask, flag = SPEAKER_FRONT_LEFT;
1144         UINT i = 0;
1145
1146         if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1147                 fmtex->dwChannelMask != 0)
1148             mask = fmtex->dwChannelMask;
1149         else
1150             mask = get_channel_mask(fmt->nChannels);
1151
1152         This->alsa_channels = 0;
1153
1154         while(i < fmt->nChannels && !(flag & SPEAKER_RESERVED)){
1155             if(mask & flag){
1156                 This->alsa_channel_map[i] = alsa_channel_index(flag);
1157                 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
1158                         i, flag, This->alsa_channel_map[i]);
1159                 if(This->alsa_channel_map[i] >= This->alsa_channels)
1160                     This->alsa_channels = This->alsa_channel_map[i] + 1;
1161                 ++i;
1162             }
1163             flag <<= 1;
1164         }
1165
1166         while(i < fmt->nChannels){
1167             This->alsa_channel_map[i] = This->alsa_channels;
1168             TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
1169                     i, This->alsa_channel_map[i]);
1170             ++This->alsa_channels;
1171             ++i;
1172         }
1173
1174         for(i = 0; i < fmt->nChannels; ++i){
1175             if(This->alsa_channel_map[i] == -1){
1176                 This->alsa_channel_map[i] = This->alsa_channels;
1177                 ++This->alsa_channels;
1178                 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
1179                         i, This->alsa_channel_map[i]);
1180             }
1181         }
1182
1183         This->need_remapping = need_remapping(This, fmt);
1184
1185         TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
1186     }else{
1187         This->need_remapping = FALSE;
1188         This->alsa_channels = fmt->nChannels;
1189         TRACE("need_remapping: %u, alsa_channels: %d\n", This->need_remapping, This->alsa_channels);
1190     }
1191
1192     return S_OK;
1193 }
1194
1195 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
1196         AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
1197         REFERENCE_TIME period, const WAVEFORMATEX *fmt,
1198         const GUID *sessionguid)
1199 {
1200     ACImpl *This = impl_from_IAudioClient(iface);
1201     snd_pcm_sw_params_t *sw_params = NULL;
1202     snd_pcm_format_t format;
1203     unsigned int rate, alsa_period_us;
1204     int err, i;
1205     HRESULT hr = S_OK;
1206
1207     TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
1208           wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
1209
1210     if(!fmt)
1211         return E_POINTER;
1212
1213     if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1214         return AUDCLNT_E_NOT_INITIALIZED;
1215
1216     if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
1217                 AUDCLNT_STREAMFLAGS_LOOPBACK |
1218                 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
1219                 AUDCLNT_STREAMFLAGS_NOPERSIST |
1220                 AUDCLNT_STREAMFLAGS_RATEADJUST |
1221                 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
1222                 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
1223                 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
1224         TRACE("Unknown flags: %08x\n", flags);
1225         return E_INVALIDARG;
1226     }
1227
1228     if(mode == AUDCLNT_SHAREMODE_SHARED){
1229         period = DefaultPeriod;
1230         if( duration < 3 * period)
1231             duration = 3 * period;
1232     }else{
1233         if(!period)
1234             period = DefaultPeriod; /* not minimum */
1235         if(period < MinimumPeriod || period > 5000000)
1236             return AUDCLNT_E_INVALID_DEVICE_PERIOD;
1237         if(duration > 20000000) /* the smaller the period, the lower this limit */
1238             return AUDCLNT_E_BUFFER_SIZE_ERROR;
1239         if(flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK){
1240             if(duration != period)
1241                 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
1242             FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
1243             return AUDCLNT_E_DEVICE_IN_USE;
1244         }else{
1245             if( duration < 8 * period)
1246                 duration = 8 * period; /* may grow above 2s */
1247         }
1248     }
1249
1250     EnterCriticalSection(&This->lock);
1251
1252     if(This->initted){
1253         LeaveCriticalSection(&This->lock);
1254         return AUDCLNT_E_ALREADY_INITIALIZED;
1255     }
1256
1257     dump_fmt(fmt);
1258
1259     if(FAILED(map_channels(This, fmt))){
1260         WARN("map_channels failed\n");
1261         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1262         goto exit;
1263     }
1264
1265     if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1266         WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1267         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1268         goto exit;
1269     }
1270
1271     if((err = snd_pcm_hw_params_set_access(This->pcm_handle, This->hw_params,
1272                 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){
1273         WARN("Unable to set access: %d (%s)\n", err, snd_strerror(err));
1274         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1275         goto exit;
1276     }
1277
1278     format = alsa_format(fmt);
1279     if (format == SND_PCM_FORMAT_UNKNOWN){
1280         hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1281         goto exit;
1282     }
1283
1284     if((err = snd_pcm_hw_params_set_format(This->pcm_handle, This->hw_params,
1285                 format)) < 0){
1286         WARN("Unable to set ALSA format to %u: %d (%s)\n", format, err,
1287                 snd_strerror(err));
1288         hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1289         goto exit;
1290     }
1291
1292     This->alsa_format = format;
1293
1294     rate = fmt->nSamplesPerSec;
1295     if((err = snd_pcm_hw_params_set_rate_near(This->pcm_handle, This->hw_params,
1296                 &rate, NULL)) < 0){
1297         WARN("Unable to set rate to %u: %d (%s)\n", rate, err,
1298                 snd_strerror(err));
1299         hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1300         goto exit;
1301     }
1302
1303     if((err = snd_pcm_hw_params_set_channels(This->pcm_handle, This->hw_params,
1304                 This->alsa_channels)) < 0){
1305         WARN("Unable to set channels to %u: %d (%s)\n", fmt->nChannels, err,
1306                 snd_strerror(err));
1307         hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1308         goto exit;
1309     }
1310
1311     This->mmdev_period_rt = period;
1312     alsa_period_us = This->mmdev_period_rt / 10;
1313     if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
1314                 This->hw_params, &alsa_period_us, NULL)) < 0)
1315         WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
1316                 err, snd_strerror(err));
1317     /* ALSA updates the output variable alsa_period_us */
1318
1319     This->mmdev_period_frames = MulDiv(fmt->nSamplesPerSec,
1320             This->mmdev_period_rt, 10000000);
1321
1322     /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
1323     This->alsa_bufsize_frames = This->mmdev_period_frames * 4;
1324     if(err < 0 || alsa_period_us < period / 10)
1325         err = snd_pcm_hw_params_set_buffer_size_near(This->pcm_handle,
1326                 This->hw_params, &This->alsa_bufsize_frames);
1327     else{
1328         unsigned int periods = 4;
1329         err = snd_pcm_hw_params_set_periods_near(This->pcm_handle, This->hw_params, &periods, NULL);
1330     }
1331     if(err < 0)
1332         WARN("Unable to set buffer size: %d (%s)\n", err, snd_strerror(err));
1333
1334     if((err = snd_pcm_hw_params(This->pcm_handle, This->hw_params)) < 0){
1335         WARN("Unable to set hw params: %d (%s)\n", err, snd_strerror(err));
1336         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1337         goto exit;
1338     }
1339
1340     if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
1341                     &This->alsa_period_frames, NULL)) < 0){
1342         WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
1343         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1344         goto exit;
1345     }
1346
1347     if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
1348                     &This->alsa_bufsize_frames)) < 0){
1349         WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
1350         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1351         goto exit;
1352     }
1353
1354     sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof());
1355     if(!sw_params){
1356         hr = E_OUTOFMEMORY;
1357         goto exit;
1358     }
1359
1360     if((err = snd_pcm_sw_params_current(This->pcm_handle, sw_params)) < 0){
1361         WARN("Unable to get sw_params: %d (%s)\n", err, snd_strerror(err));
1362         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1363         goto exit;
1364     }
1365
1366     if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
1367                     sw_params, 1)) < 0){
1368         WARN("Unable set start threshold to 0: %d (%s)\n", err, snd_strerror(err));
1369         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1370         goto exit;
1371     }
1372
1373     if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
1374                     sw_params, This->alsa_bufsize_frames)) < 0){
1375         WARN("Unable set stop threshold to %lu: %d (%s)\n",
1376                 This->alsa_bufsize_frames, err, snd_strerror(err));
1377         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1378         goto exit;
1379     }
1380
1381     if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
1382         WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
1383         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1384         goto exit;
1385     }
1386
1387     if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
1388         WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
1389         hr = AUDCLNT_E_ENDPOINT_CREATE_FAILED;
1390         goto exit;
1391     }
1392
1393     /* Bear in mind weird situations where
1394      * ALSA period (50ms) > mmdevapi buffer (3x10ms)
1395      * or surprising rounding as seen with 22050x8x1 with Pulse:
1396      * ALSA period 220 vs.  221 frames in mmdevapi and
1397      *      buffer 883 vs. 2205 frames in mmdevapi! */
1398     This->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000);
1399     This->hidden_frames = This->alsa_period_frames + This->mmdev_period_frames +
1400         MulDiv(fmt->nSamplesPerSec, EXTRA_SAFE_RT, 10000000);
1401
1402     /* Check if the ALSA buffer is so small that it will run out before
1403      * the next MMDevAPI period tick occurs. Allow a little wiggle room
1404      * with 120% of the period time. */
1405     if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
1406         FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1407                 This->alsa_bufsize_frames, This->mmdev_period_frames);
1408
1409     This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
1410             This->bufsize_frames * fmt->nBlockAlign);
1411     if(!This->local_buffer){
1412         hr = E_OUTOFMEMORY;
1413         goto exit;
1414     }
1415     if (fmt->wBitsPerSample == 8)
1416         memset(This->local_buffer, 128, This->bufsize_frames * fmt->nBlockAlign);
1417     else
1418         memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
1419
1420     This->fmt = clone_format(fmt);
1421     if(!This->fmt){
1422         hr = E_OUTOFMEMORY;
1423         goto exit;
1424     }
1425
1426     This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
1427     if(!This->vols){
1428         hr = E_OUTOFMEMORY;
1429         goto exit;
1430     }
1431
1432     for(i = 0; i < fmt->nChannels; ++i)
1433         This->vols[i] = 1.f;
1434
1435     This->share = mode;
1436     This->flags = flags;
1437
1438     EnterCriticalSection(&g_sessions_lock);
1439
1440     hr = get_audio_session(sessionguid, This->parent, fmt->nChannels,
1441             &This->session);
1442     if(FAILED(hr)){
1443         LeaveCriticalSection(&g_sessions_lock);
1444         goto exit;
1445     }
1446
1447     list_add_tail(&This->session->clients, &This->entry);
1448
1449     LeaveCriticalSection(&g_sessions_lock);
1450
1451     This->initted = TRUE;
1452
1453     TRACE("ALSA period: %lu frames\n", This->alsa_period_frames);
1454     TRACE("ALSA buffer: %lu frames\n", This->alsa_bufsize_frames);
1455     TRACE("MMDevice period: %u frames\n", This->mmdev_period_frames);
1456     TRACE("MMDevice buffer: %u frames\n", This->bufsize_frames);
1457
1458 exit:
1459     HeapFree(GetProcessHeap(), 0, sw_params);
1460     if(FAILED(hr)){
1461         HeapFree(GetProcessHeap(), 0, This->local_buffer);
1462         This->local_buffer = NULL;
1463         CoTaskMemFree(This->fmt);
1464         This->fmt = NULL;
1465         HeapFree(GetProcessHeap(), 0, This->vols);
1466         This->vols = NULL;
1467     }
1468
1469     LeaveCriticalSection(&This->lock);
1470
1471     return hr;
1472 }
1473
1474 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
1475         UINT32 *out)
1476 {
1477     ACImpl *This = impl_from_IAudioClient(iface);
1478
1479     TRACE("(%p)->(%p)\n", This, out);
1480
1481     if(!out)
1482         return E_POINTER;
1483
1484     EnterCriticalSection(&This->lock);
1485
1486     if(!This->initted){
1487         LeaveCriticalSection(&This->lock);
1488         return AUDCLNT_E_NOT_INITIALIZED;
1489     }
1490
1491     *out = This->bufsize_frames;
1492
1493     LeaveCriticalSection(&This->lock);
1494
1495     return S_OK;
1496 }
1497
1498 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
1499         REFERENCE_TIME *latency)
1500 {
1501     ACImpl *This = impl_from_IAudioClient(iface);
1502
1503     TRACE("(%p)->(%p)\n", This, latency);
1504
1505     if(!latency)
1506         return E_POINTER;
1507
1508     EnterCriticalSection(&This->lock);
1509
1510     if(!This->initted){
1511         LeaveCriticalSection(&This->lock);
1512         return AUDCLNT_E_NOT_INITIALIZED;
1513     }
1514
1515     /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
1516      * yet have enough data left to play (as if it were in native's mixer). Add:
1517      * + mmdevapi_period such that at the end of it, ALSA still has data;
1518      * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
1519      * + alsa_period such that ALSA always has at least one period to play. */
1520     if(This->dataflow == eRender)
1521         *latency = MulDiv(This->hidden_frames, 10000000, This->fmt->nSamplesPerSec);
1522     else
1523         *latency = MulDiv(This->alsa_period_frames, 10000000, This->fmt->nSamplesPerSec)
1524                  + This->mmdev_period_rt;
1525
1526     LeaveCriticalSection(&This->lock);
1527
1528     return S_OK;
1529 }
1530
1531 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
1532         UINT32 *out)
1533 {
1534     ACImpl *This = impl_from_IAudioClient(iface);
1535
1536     TRACE("(%p)->(%p)\n", This, out);
1537
1538     if(!out)
1539         return E_POINTER;
1540
1541     EnterCriticalSection(&This->lock);
1542
1543     if(!This->initted){
1544         LeaveCriticalSection(&This->lock);
1545         return AUDCLNT_E_NOT_INITIALIZED;
1546     }
1547
1548     /* padding is solely updated at callback time in shared mode */
1549     *out = This->held_frames;
1550
1551     LeaveCriticalSection(&This->lock);
1552
1553     TRACE("pad: %u\n", *out);
1554
1555     return S_OK;
1556 }
1557
1558 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
1559         AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
1560         WAVEFORMATEX **out)
1561 {
1562     ACImpl *This = impl_from_IAudioClient(iface);
1563     snd_pcm_format_mask_t *formats = NULL;
1564     snd_pcm_format_t format;
1565     HRESULT hr = S_OK;
1566     WAVEFORMATEX *closest = NULL;
1567     unsigned int max = 0, min = 0;
1568     int err;
1569
1570     TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
1571
1572     if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
1573         return E_POINTER;
1574
1575     if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
1576         return E_INVALIDARG;
1577
1578     if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1579             fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1580         return E_INVALIDARG;
1581
1582     dump_fmt(fmt);
1583
1584     if(out){
1585         *out = NULL;
1586         if(mode != AUDCLNT_SHAREMODE_SHARED)
1587             out = NULL;
1588     }
1589
1590     EnterCriticalSection(&This->lock);
1591
1592     if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1593         hr = AUDCLNT_E_DEVICE_INVALIDATED;
1594         goto exit;
1595     }
1596
1597     formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1598             snd_pcm_format_mask_sizeof());
1599     if(!formats){
1600         hr = E_OUTOFMEMORY;
1601         goto exit;
1602     }
1603
1604     snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1605     format = alsa_format(fmt);
1606     if (format == SND_PCM_FORMAT_UNKNOWN ||
1607         !snd_pcm_format_mask_test(formats, format)){
1608         hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1609         goto exit;
1610     }
1611
1612     closest = clone_format(fmt);
1613     if(!closest){
1614         hr = E_OUTOFMEMORY;
1615         goto exit;
1616     }
1617
1618     if((err = snd_pcm_hw_params_get_rate_min(This->hw_params, &min, NULL)) < 0){
1619         hr = AUDCLNT_E_DEVICE_INVALIDATED;
1620         WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
1621         goto exit;
1622     }
1623
1624     if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max, NULL)) < 0){
1625         hr = AUDCLNT_E_DEVICE_INVALIDATED;
1626         WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1627         goto exit;
1628     }
1629
1630     if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
1631         hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1632         goto exit;
1633     }
1634
1635     if((err = snd_pcm_hw_params_get_channels_min(This->hw_params, &min)) < 0){
1636         hr = AUDCLNT_E_DEVICE_INVALIDATED;
1637         WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
1638         goto exit;
1639     }
1640
1641     if((err = snd_pcm_hw_params_get_channels_max(This->hw_params, &max)) < 0){
1642         hr = AUDCLNT_E_DEVICE_INVALIDATED;
1643         WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1644         goto exit;
1645     }
1646     if(fmt->nChannels > max){
1647         hr = S_FALSE;
1648         closest->nChannels = max;
1649     }else if(fmt->nChannels < min){
1650         hr = S_FALSE;
1651         closest->nChannels = min;
1652     }
1653
1654     if(FAILED(map_channels(This, fmt))){
1655         hr = AUDCLNT_E_DEVICE_INVALIDATED;
1656         WARN("map_channels failed\n");
1657         goto exit;
1658     }
1659     if(This->alsa_channels > max){
1660         hr = S_FALSE;
1661         closest->nChannels = max;
1662     }
1663
1664     if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1665         ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
1666
1667 exit:
1668     LeaveCriticalSection(&This->lock);
1669     HeapFree(GetProcessHeap(), 0, formats);
1670
1671     if(hr == S_FALSE && !out)
1672         hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1673
1674     if(hr == S_FALSE && out) {
1675         closest->nBlockAlign =
1676             closest->nChannels * closest->wBitsPerSample / 8;
1677         closest->nAvgBytesPerSec =
1678             closest->nBlockAlign * closest->nSamplesPerSec;
1679         *out = closest;
1680     } else
1681         CoTaskMemFree(closest);
1682
1683     TRACE("returning: %08x\n", hr);
1684     return hr;
1685 }
1686
1687 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
1688         WAVEFORMATEX **pwfx)
1689 {
1690     ACImpl *This = impl_from_IAudioClient(iface);
1691     WAVEFORMATEXTENSIBLE *fmt;
1692     snd_pcm_format_mask_t *formats;
1693     unsigned int max_rate, max_channels;
1694     int err;
1695     HRESULT hr = S_OK;
1696
1697     TRACE("(%p)->(%p)\n", This, pwfx);
1698
1699     if(!pwfx)
1700         return E_POINTER;
1701     *pwfx = NULL;
1702
1703     fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1704     if(!fmt)
1705         return E_OUTOFMEMORY;
1706
1707     formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
1708     if(!formats){
1709         CoTaskMemFree(fmt);
1710         return E_OUTOFMEMORY;
1711     }
1712
1713     EnterCriticalSection(&This->lock);
1714
1715     if((err = snd_pcm_hw_params_any(This->pcm_handle, This->hw_params)) < 0){
1716         WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
1717         hr = AUDCLNT_E_DEVICE_INVALIDATED;
1718         goto exit;
1719     }
1720
1721     snd_pcm_hw_params_get_format_mask(This->hw_params, formats);
1722
1723     fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1724     if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
1725         fmt->Format.wBitsPerSample = 32;
1726         fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1727     }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
1728         fmt->Format.wBitsPerSample = 16;
1729         fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1730     }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
1731         fmt->Format.wBitsPerSample = 8;
1732         fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1733     }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
1734         fmt->Format.wBitsPerSample = 32;
1735         fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1736     }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
1737         fmt->Format.wBitsPerSample = 24;
1738         fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1739     }else{
1740         ERR("Didn't recognize any available ALSA formats\n");
1741         hr = AUDCLNT_E_DEVICE_INVALIDATED;
1742         goto exit;
1743     }
1744
1745     if((err = snd_pcm_hw_params_get_channels_max(This->hw_params,
1746                     &max_channels)) < 0){
1747         WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
1748         hr = AUDCLNT_E_DEVICE_INVALIDATED;
1749         goto exit;
1750     }
1751
1752     if(max_channels > 2)
1753         fmt->Format.nChannels = 2;
1754     else
1755         fmt->Format.nChannels = max_channels;
1756
1757     fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1758
1759     if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
1760                     NULL)) < 0){
1761         WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
1762         hr = AUDCLNT_E_DEVICE_INVALIDATED;
1763         goto exit;
1764     }
1765
1766     if(max_rate >= 48000)
1767         fmt->Format.nSamplesPerSec = 48000;
1768     else if(max_rate >= 44100)
1769         fmt->Format.nSamplesPerSec = 44100;
1770     else if(max_rate >= 22050)
1771         fmt->Format.nSamplesPerSec = 22050;
1772     else if(max_rate >= 11025)
1773         fmt->Format.nSamplesPerSec = 11025;
1774     else if(max_rate >= 8000)
1775         fmt->Format.nSamplesPerSec = 8000;
1776     else{
1777         ERR("Unknown max rate: %u\n", max_rate);
1778         hr = AUDCLNT_E_DEVICE_INVALIDATED;
1779         goto exit;
1780     }
1781
1782     fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1783             fmt->Format.nChannels) / 8;
1784     fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1785         fmt->Format.nBlockAlign;
1786
1787     fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1788     fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1789
1790     dump_fmt((WAVEFORMATEX*)fmt);
1791     *pwfx = (WAVEFORMATEX*)fmt;
1792
1793 exit:
1794     LeaveCriticalSection(&This->lock);
1795     if(FAILED(hr))
1796         CoTaskMemFree(fmt);
1797     HeapFree(GetProcessHeap(), 0, formats);
1798
1799     return hr;
1800 }
1801
1802 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1803         REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1804 {
1805     ACImpl *This = impl_from_IAudioClient(iface);
1806
1807     TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1808
1809     if(!defperiod && !minperiod)
1810         return E_POINTER;
1811
1812     if(defperiod)
1813         *defperiod = DefaultPeriod;
1814     if(minperiod)
1815         *minperiod = MinimumPeriod;
1816
1817     return S_OK;
1818 }
1819
1820 static BYTE *remap_channels(ACImpl *This, BYTE *buf, snd_pcm_uframes_t frames)
1821 {
1822     snd_pcm_uframes_t i;
1823     UINT c;
1824     UINT bytes_per_sample = This->fmt->wBitsPerSample / 8;
1825
1826     if(!This->need_remapping)
1827         return buf;
1828
1829     if(!This->remapping_buf){
1830         This->remapping_buf = HeapAlloc(GetProcessHeap(), 0,
1831                 bytes_per_sample * This->alsa_channels * frames);
1832         This->remapping_buf_frames = frames;
1833     }else if(This->remapping_buf_frames < frames){
1834         This->remapping_buf = HeapReAlloc(GetProcessHeap(), 0, This->remapping_buf,
1835                 bytes_per_sample * This->alsa_channels * frames);
1836         This->remapping_buf_frames = frames;
1837     }
1838
1839     snd_pcm_format_set_silence(This->alsa_format, This->remapping_buf,
1840             frames * This->alsa_channels);
1841
1842     switch(This->fmt->wBitsPerSample){
1843     case 8: {
1844             UINT8 *tgt_buf, *src_buf;
1845             tgt_buf = This->remapping_buf;
1846             src_buf = buf;
1847             for(i = 0; i < frames; ++i){
1848                 for(c = 0; c < This->fmt->nChannels; ++c)
1849                     tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1850                 tgt_buf += This->alsa_channels;
1851                 src_buf += This->fmt->nChannels;
1852             }
1853             break;
1854         }
1855     case 16: {
1856             UINT16 *tgt_buf, *src_buf;
1857             tgt_buf = (UINT16*)This->remapping_buf;
1858             src_buf = (UINT16*)buf;
1859             for(i = 0; i < frames; ++i){
1860                 for(c = 0; c < This->fmt->nChannels; ++c)
1861                     tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1862                 tgt_buf += This->alsa_channels;
1863                 src_buf += This->fmt->nChannels;
1864             }
1865         }
1866         break;
1867     case 32: {
1868             UINT32 *tgt_buf, *src_buf;
1869             tgt_buf = (UINT32*)This->remapping_buf;
1870             src_buf = (UINT32*)buf;
1871             for(i = 0; i < frames; ++i){
1872                 for(c = 0; c < This->fmt->nChannels; ++c)
1873                     tgt_buf[This->alsa_channel_map[c]] = src_buf[c];
1874                 tgt_buf += This->alsa_channels;
1875                 src_buf += This->fmt->nChannels;
1876             }
1877         }
1878         break;
1879     default: {
1880             BYTE *tgt_buf, *src_buf;
1881             tgt_buf = This->remapping_buf;
1882             src_buf = buf;
1883             for(i = 0; i < frames; ++i){
1884                 for(c = 0; c < This->fmt->nChannels; ++c)
1885                     memcpy(&tgt_buf[This->alsa_channel_map[c] * bytes_per_sample],
1886                             &src_buf[c * bytes_per_sample], bytes_per_sample);
1887                 tgt_buf += This->alsa_channels * bytes_per_sample;
1888                 src_buf += This->fmt->nChannels * bytes_per_sample;
1889             }
1890         }
1891         break;
1892     }
1893
1894     return This->remapping_buf;
1895 }
1896
1897 static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
1898         snd_pcm_uframes_t frames, ACImpl *This, BOOL mute)
1899 {
1900     snd_pcm_sframes_t written;
1901
1902     if(mute){
1903         int err;
1904         if((err = snd_pcm_format_set_silence(This->alsa_format, buf,
1905                         frames * This->fmt->nChannels)) < 0)
1906             WARN("Setting buffer to silence failed: %d (%s)\n", err,
1907                     snd_strerror(err));
1908     }
1909
1910     buf = remap_channels(This, buf, frames);
1911
1912     written = snd_pcm_writei(handle, buf, frames);
1913     if(written < 0){
1914         int ret;
1915
1916         if(written == -EAGAIN)
1917             /* buffer full */
1918             return 0;
1919
1920         WARN("writei failed, recovering: %ld (%s)\n", written,
1921                 snd_strerror(written));
1922
1923         ret = snd_pcm_recover(handle, written, 0);
1924         if(ret < 0){
1925             WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
1926             return ret;
1927         }
1928
1929         written = snd_pcm_writei(handle, buf, frames);
1930     }
1931
1932     return written;
1933 }
1934
1935 static void alsa_write_data(ACImpl *This)
1936 {
1937     snd_pcm_sframes_t written, in_alsa;
1938     snd_pcm_uframes_t to_write, avail, write_limit, max_period;
1939     int err;
1940     BYTE *buf =
1941         This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1942
1943     /* this call seems to be required to get an accurate snd_pcm_state() */
1944     avail = snd_pcm_avail_update(This->pcm_handle);
1945
1946     if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN ||
1947             avail > This->alsa_bufsize_frames){
1948         TRACE("XRun state avail %ld, recovering\n", avail);
1949
1950         avail = This->alsa_bufsize_frames;
1951
1952         if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
1953             WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
1954
1955         if((err = snd_pcm_reset(This->pcm_handle)) < 0)
1956             WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
1957
1958         if((err = snd_pcm_prepare(This->pcm_handle)) < 0)
1959             WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
1960     }else
1961         TRACE("pad: %ld\n", This->alsa_bufsize_frames - avail);
1962
1963     if(This->held_frames == 0)
1964         return;
1965
1966     if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1967         to_write = This->bufsize_frames - This->lcl_offs_frames;
1968     else
1969         to_write = This->held_frames;
1970
1971     max_period = max(This->mmdev_period_frames, This->alsa_period_frames);
1972
1973     /* try to keep 3 ALSA periods or 3 MMDevAPI periods in the ALSA buffer and
1974      * no more */
1975     write_limit = 0;
1976     in_alsa = This->alsa_bufsize_frames - avail;
1977     while(in_alsa + write_limit < max_period * 3)
1978         write_limit += max_period;
1979     if(write_limit == 0)
1980         return;
1981
1982     to_write = min(to_write, write_limit);
1983
1984     /* Add a lead-in when starting with too few frames to ensure
1985      * continuous rendering.  Additional benefit: Force ALSA to start.
1986      * GetPosition continues to reflect the speaker position because
1987      * snd_pcm_delay includes buffered frames in its total delay
1988      * and last_pos_frames prevents moving backwards. */
1989     if(!in_alsa && This->held_frames < This->hidden_frames){
1990         UINT32 s_frames = This->hidden_frames - This->held_frames;
1991         BYTE *silence = HeapAlloc(GetProcessHeap(), 0,
1992                 s_frames * This->fmt->nBlockAlign);
1993
1994         if(silence){
1995             in_alsa = alsa_write_best_effort(This->pcm_handle,
1996                 silence, s_frames, This, TRUE);
1997             TRACE("lead-in %ld\n", in_alsa);
1998             HeapFree(GetProcessHeap(), 0, silence);
1999             if(in_alsa <= 0)
2000                 return;
2001         }else
2002             WARN("Couldn't allocate lead-in, expect underrun\n");
2003     }
2004
2005     written = alsa_write_best_effort(This->pcm_handle, buf, to_write, This,
2006             This->session->mute);
2007     if(written < 0){
2008         WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
2009         return;
2010     }
2011
2012     This->lcl_offs_frames += written;
2013     This->lcl_offs_frames %= This->bufsize_frames;
2014     This->held_frames -= written;
2015
2016     if(written < to_write){
2017         /* ALSA buffer probably full */
2018         return;
2019     }
2020
2021     if(This->held_frames && (written < write_limit)){
2022         /* wrapped and have some data back at the start to write */
2023         written = alsa_write_best_effort(This->pcm_handle, This->local_buffer,
2024                 min(This->held_frames, write_limit - written), This,
2025                 This->session->mute);
2026         if(written < 0){
2027             WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
2028             return;
2029         }
2030
2031         This->lcl_offs_frames += written;
2032         This->lcl_offs_frames %= This->bufsize_frames;
2033         This->held_frames -= written;
2034     }
2035 }
2036
2037 static void alsa_read_data(ACImpl *This)
2038 {
2039     snd_pcm_sframes_t pos, readable, nread;
2040
2041     pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
2042     readable = This->bufsize_frames - pos;
2043
2044     nread = snd_pcm_readi(This->pcm_handle,
2045             This->local_buffer + pos * This->fmt->nBlockAlign, readable);
2046     TRACE("read %ld from %u limit %lu\n", nread, This->held_frames + This->lcl_offs_frames, readable);
2047     if(nread < 0){
2048         int ret;
2049
2050         if(nread == -EAGAIN) /* no data yet */
2051             return;
2052
2053         WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
2054
2055         ret = snd_pcm_recover(This->pcm_handle, nread, 0);
2056         if(ret < 0){
2057             WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
2058             return;
2059         }
2060
2061         nread = snd_pcm_readi(This->pcm_handle,
2062                 This->local_buffer + pos * This->fmt->nBlockAlign, readable);
2063         if(nread < 0){
2064             WARN("read failed: %ld (%s)\n", nread, snd_strerror(nread));
2065             return;
2066         }
2067     }
2068
2069     if(This->session->mute){
2070         int err;
2071         if((err = snd_pcm_format_set_silence(This->alsa_format,
2072                         This->local_buffer + pos * This->fmt->nBlockAlign,
2073                         nread)) < 0)
2074             WARN("Setting buffer to silence failed: %d (%s)\n", err,
2075                     snd_strerror(err));
2076     }
2077
2078     This->held_frames += nread;
2079
2080     if(This->held_frames > This->bufsize_frames){
2081         WARN("Overflow of unread data\n");
2082         This->lcl_offs_frames += This->held_frames;
2083         This->lcl_offs_frames %= This->bufsize_frames;
2084         This->held_frames = This->bufsize_frames;
2085     }
2086 }
2087
2088 static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
2089 {
2090     ACImpl *This = user;
2091
2092     EnterCriticalSection(&This->lock);
2093
2094     if(This->started){
2095         if(This->dataflow == eRender)
2096             alsa_write_data(This);
2097         else if(This->dataflow == eCapture)
2098             alsa_read_data(This);
2099
2100         if(This->event)
2101             SetEvent(This->event);
2102     }
2103
2104     LeaveCriticalSection(&This->lock);
2105 }
2106
2107 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
2108 {
2109     ACImpl *This = impl_from_IAudioClient(iface);
2110
2111     TRACE("(%p)\n", This);
2112
2113     EnterCriticalSection(&This->lock);
2114
2115     if(!This->initted){
2116         LeaveCriticalSection(&This->lock);
2117         return AUDCLNT_E_NOT_INITIALIZED;
2118     }
2119
2120     if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
2121         LeaveCriticalSection(&This->lock);
2122         return AUDCLNT_E_EVENTHANDLE_NOT_SET;
2123     }
2124
2125     if(This->started){
2126         LeaveCriticalSection(&This->lock);
2127         return AUDCLNT_E_NOT_STOPPED;
2128     }
2129
2130     if(This->dataflow == eCapture){
2131         /* dump any data that might be leftover in the ALSA capture buffer */
2132         snd_pcm_readi(This->pcm_handle, This->local_buffer,
2133                 This->bufsize_frames);
2134     }
2135
2136     if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
2137             This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
2138         LeaveCriticalSection(&This->lock);
2139         WARN("Unable to create timer: %u\n", GetLastError());
2140         return E_OUTOFMEMORY;
2141     }
2142
2143     This->started = TRUE;
2144
2145     LeaveCriticalSection(&This->lock);
2146
2147     return S_OK;
2148 }
2149
2150 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
2151 {
2152     ACImpl *This = impl_from_IAudioClient(iface);
2153     HANDLE event;
2154     BOOL wait;
2155
2156     TRACE("(%p)\n", This);
2157
2158     EnterCriticalSection(&This->lock);
2159
2160     if(!This->initted){
2161         LeaveCriticalSection(&This->lock);
2162         return AUDCLNT_E_NOT_INITIALIZED;
2163     }
2164
2165     if(!This->started){
2166         LeaveCriticalSection(&This->lock);
2167         return S_FALSE;
2168     }
2169
2170     /* Stop without losing written frames or position.
2171      * snd_pcm_pause would be appropriate but is unsupported by dmix.
2172      * snd_pcm_drain yields EAGAIN in NONBLOCK mode, except with Pulse. */
2173
2174     event = CreateEventW(NULL, TRUE, FALSE, NULL);
2175     wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
2176     if(wait)
2177         WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
2178     wait = wait && GetLastError() == ERROR_IO_PENDING;
2179
2180     This->started = FALSE;
2181
2182     LeaveCriticalSection(&This->lock);
2183
2184     if(event && wait)
2185         WaitForSingleObject(event, INFINITE);
2186     CloseHandle(event);
2187
2188     return S_OK;
2189 }
2190
2191 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
2192 {
2193     ACImpl *This = impl_from_IAudioClient(iface);
2194
2195     TRACE("(%p)\n", This);
2196
2197     EnterCriticalSection(&This->lock);
2198
2199     if(!This->initted){
2200         LeaveCriticalSection(&This->lock);
2201         return AUDCLNT_E_NOT_INITIALIZED;
2202     }
2203
2204     if(This->started){
2205         LeaveCriticalSection(&This->lock);
2206         return AUDCLNT_E_NOT_STOPPED;
2207     }
2208
2209     if(This->getbuf_last){
2210         LeaveCriticalSection(&This->lock);
2211         return AUDCLNT_E_BUFFER_OPERATION_PENDING;
2212     }
2213
2214     if(snd_pcm_drop(This->pcm_handle) < 0)
2215         WARN("snd_pcm_drop failed\n");
2216
2217     if(snd_pcm_reset(This->pcm_handle) < 0)
2218         WARN("snd_pcm_reset failed\n");
2219
2220     if(snd_pcm_prepare(This->pcm_handle) < 0)
2221         WARN("snd_pcm_prepare failed\n");
2222
2223     if(This->dataflow == eRender){
2224         This->written_frames = 0;
2225         This->last_pos_frames = 0;
2226     }else{
2227         This->written_frames += This->held_frames;
2228     }
2229     This->held_frames = 0;
2230     This->lcl_offs_frames = 0;
2231
2232     LeaveCriticalSection(&This->lock);
2233
2234     return S_OK;
2235 }
2236
2237 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
2238         HANDLE event)
2239 {
2240     ACImpl *This = impl_from_IAudioClient(iface);
2241
2242     TRACE("(%p)->(%p)\n", This, event);
2243
2244     if(!event)
2245         return E_INVALIDARG;
2246
2247     EnterCriticalSection(&This->lock);
2248
2249     if(!This->initted){
2250         LeaveCriticalSection(&This->lock);
2251         return AUDCLNT_E_NOT_INITIALIZED;
2252     }
2253
2254     if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
2255         LeaveCriticalSection(&This->lock);
2256         return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
2257     }
2258
2259     This->event = event;
2260
2261     LeaveCriticalSection(&This->lock);
2262
2263     return S_OK;
2264 }
2265
2266 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
2267         void **ppv)
2268 {
2269     ACImpl *This = impl_from_IAudioClient(iface);
2270
2271     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
2272
2273     if(!ppv)
2274         return E_POINTER;
2275     *ppv = NULL;
2276
2277     EnterCriticalSection(&This->lock);
2278
2279     if(!This->initted){
2280         LeaveCriticalSection(&This->lock);
2281         return AUDCLNT_E_NOT_INITIALIZED;
2282     }
2283
2284     if(IsEqualIID(riid, &IID_IAudioRenderClient)){
2285         if(This->dataflow != eRender){
2286             LeaveCriticalSection(&This->lock);
2287             return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2288         }
2289         IAudioRenderClient_AddRef(&This->IAudioRenderClient_iface);
2290         *ppv = &This->IAudioRenderClient_iface;
2291     }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
2292         if(This->dataflow != eCapture){
2293             LeaveCriticalSection(&This->lock);
2294             return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
2295         }
2296         IAudioCaptureClient_AddRef(&This->IAudioCaptureClient_iface);
2297         *ppv = &This->IAudioCaptureClient_iface;
2298     }else if(IsEqualIID(riid, &IID_IAudioClock)){
2299         IAudioClock_AddRef(&This->IAudioClock_iface);
2300         *ppv = &This->IAudioClock_iface;
2301     }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
2302         IAudioStreamVolume_AddRef(&This->IAudioStreamVolume_iface);
2303         *ppv = &This->IAudioStreamVolume_iface;
2304     }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
2305         if(!This->session_wrapper){
2306             This->session_wrapper = AudioSessionWrapper_Create(This);
2307             if(!This->session_wrapper){
2308                 LeaveCriticalSection(&This->lock);
2309                 return E_OUTOFMEMORY;
2310             }
2311         }else
2312             IAudioSessionControl2_AddRef(&This->session_wrapper->IAudioSessionControl2_iface);
2313
2314         *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
2315     }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
2316         if(!This->session_wrapper){
2317             This->session_wrapper = AudioSessionWrapper_Create(This);
2318             if(!This->session_wrapper){
2319                 LeaveCriticalSection(&This->lock);
2320                 return E_OUTOFMEMORY;
2321             }
2322         }else
2323             IChannelAudioVolume_AddRef(&This->session_wrapper->IChannelAudioVolume_iface);
2324
2325         *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
2326     }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
2327         if(!This->session_wrapper){
2328             This->session_wrapper = AudioSessionWrapper_Create(This);
2329             if(!This->session_wrapper){
2330                 LeaveCriticalSection(&This->lock);
2331                 return E_OUTOFMEMORY;
2332             }
2333         }else
2334             ISimpleAudioVolume_AddRef(&This->session_wrapper->ISimpleAudioVolume_iface);
2335
2336         *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
2337     }
2338
2339     if(*ppv){
2340         LeaveCriticalSection(&This->lock);
2341         return S_OK;
2342     }
2343
2344     LeaveCriticalSection(&This->lock);
2345
2346     FIXME("stub %s\n", debugstr_guid(riid));
2347     return E_NOINTERFACE;
2348 }
2349
2350 static const IAudioClientVtbl AudioClient_Vtbl =
2351 {
2352     AudioClient_QueryInterface,
2353     AudioClient_AddRef,
2354     AudioClient_Release,
2355     AudioClient_Initialize,
2356     AudioClient_GetBufferSize,
2357     AudioClient_GetStreamLatency,
2358     AudioClient_GetCurrentPadding,
2359     AudioClient_IsFormatSupported,
2360     AudioClient_GetMixFormat,
2361     AudioClient_GetDevicePeriod,
2362     AudioClient_Start,
2363     AudioClient_Stop,
2364     AudioClient_Reset,
2365     AudioClient_SetEventHandle,
2366     AudioClient_GetService
2367 };
2368
2369 static HRESULT WINAPI AudioRenderClient_QueryInterface(
2370         IAudioRenderClient *iface, REFIID riid, void **ppv)
2371 {
2372     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2373
2374     if(!ppv)
2375         return E_POINTER;
2376     *ppv = NULL;
2377
2378     if(IsEqualIID(riid, &IID_IUnknown) ||
2379             IsEqualIID(riid, &IID_IAudioRenderClient))
2380         *ppv = iface;
2381     if(*ppv){
2382         IUnknown_AddRef((IUnknown*)*ppv);
2383         return S_OK;
2384     }
2385
2386     WARN("Unknown interface %s\n", debugstr_guid(riid));
2387     return E_NOINTERFACE;
2388 }
2389
2390 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
2391 {
2392     ACImpl *This = impl_from_IAudioRenderClient(iface);
2393     return AudioClient_AddRef(&This->IAudioClient_iface);
2394 }
2395
2396 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
2397 {
2398     ACImpl *This = impl_from_IAudioRenderClient(iface);
2399     return AudioClient_Release(&This->IAudioClient_iface);
2400 }
2401
2402 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
2403         UINT32 frames, BYTE **data)
2404 {
2405     ACImpl *This = impl_from_IAudioRenderClient(iface);
2406     UINT32 write_pos;
2407
2408     TRACE("(%p)->(%u, %p)\n", This, frames, data);
2409
2410     if(!data)
2411         return E_POINTER;
2412     *data = NULL;
2413
2414     EnterCriticalSection(&This->lock);
2415
2416     if(This->getbuf_last){
2417         LeaveCriticalSection(&This->lock);
2418         return AUDCLNT_E_OUT_OF_ORDER;
2419     }
2420
2421     if(!frames){
2422         LeaveCriticalSection(&This->lock);
2423         return S_OK;
2424     }
2425
2426     /* held_frames == GetCurrentPadding_nolock(); */
2427     if(This->held_frames + frames > This->bufsize_frames){
2428         LeaveCriticalSection(&This->lock);
2429         return AUDCLNT_E_BUFFER_TOO_LARGE;
2430     }
2431
2432     write_pos =
2433         (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
2434     if(write_pos + frames > This->bufsize_frames){
2435         if(This->tmp_buffer_frames < frames){
2436             HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2437             This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2438                     frames * This->fmt->nBlockAlign);
2439             if(!This->tmp_buffer){
2440                 LeaveCriticalSection(&This->lock);
2441                 return E_OUTOFMEMORY;
2442             }
2443             This->tmp_buffer_frames = frames;
2444         }
2445         *data = This->tmp_buffer;
2446         This->getbuf_last = -frames;
2447     }else{
2448         *data = This->local_buffer + write_pos * This->fmt->nBlockAlign;
2449         This->getbuf_last = frames;
2450     }
2451
2452     LeaveCriticalSection(&This->lock);
2453
2454     return S_OK;
2455 }
2456
2457 static void alsa_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_frames)
2458 {
2459     snd_pcm_uframes_t write_offs_frames =
2460         (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
2461     UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
2462     snd_pcm_uframes_t chunk_frames = This->bufsize_frames - write_offs_frames;
2463     UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
2464     UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
2465
2466     if(written_bytes <= chunk_bytes){
2467         memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
2468     }else{
2469         memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
2470         memcpy(This->local_buffer, buffer + chunk_bytes,
2471                 written_bytes - chunk_bytes);
2472     }
2473 }
2474
2475 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
2476         IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
2477 {
2478     ACImpl *This = impl_from_IAudioRenderClient(iface);
2479     BYTE *buffer;
2480
2481     TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
2482
2483     EnterCriticalSection(&This->lock);
2484
2485     if(!written_frames){
2486         This->getbuf_last = 0;
2487         LeaveCriticalSection(&This->lock);
2488         return S_OK;
2489     }
2490
2491     if(!This->getbuf_last){
2492         LeaveCriticalSection(&This->lock);
2493         return AUDCLNT_E_OUT_OF_ORDER;
2494     }
2495
2496     if(written_frames > (This->getbuf_last >= 0 ? This->getbuf_last : -This->getbuf_last)){
2497         LeaveCriticalSection(&This->lock);
2498         return AUDCLNT_E_INVALID_SIZE;
2499     }
2500
2501     if(This->getbuf_last >= 0)
2502         buffer = This->local_buffer + This->fmt->nBlockAlign *
2503           ((This->lcl_offs_frames + This->held_frames) % This->bufsize_frames);
2504     else
2505         buffer = This->tmp_buffer;
2506
2507     if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
2508         if(This->fmt->wBitsPerSample == 8)
2509             memset(buffer, 128, written_frames * This->fmt->nBlockAlign);
2510         else
2511             memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
2512     }
2513
2514     if(This->getbuf_last < 0)
2515         alsa_wrap_buffer(This, buffer, written_frames);
2516
2517     This->held_frames += written_frames;
2518     This->written_frames += written_frames;
2519     This->getbuf_last = 0;
2520
2521     LeaveCriticalSection(&This->lock);
2522
2523     return S_OK;
2524 }
2525
2526 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
2527     AudioRenderClient_QueryInterface,
2528     AudioRenderClient_AddRef,
2529     AudioRenderClient_Release,
2530     AudioRenderClient_GetBuffer,
2531     AudioRenderClient_ReleaseBuffer
2532 };
2533
2534 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
2535         IAudioCaptureClient *iface, REFIID riid, void **ppv)
2536 {
2537     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2538
2539     if(!ppv)
2540         return E_POINTER;
2541     *ppv = NULL;
2542
2543     if(IsEqualIID(riid, &IID_IUnknown) ||
2544             IsEqualIID(riid, &IID_IAudioCaptureClient))
2545         *ppv = iface;
2546     if(*ppv){
2547         IUnknown_AddRef((IUnknown*)*ppv);
2548         return S_OK;
2549     }
2550
2551     WARN("Unknown interface %s\n", debugstr_guid(riid));
2552     return E_NOINTERFACE;
2553 }
2554
2555 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
2556 {
2557     ACImpl *This = impl_from_IAudioCaptureClient(iface);
2558     return IAudioClient_AddRef(&This->IAudioClient_iface);
2559 }
2560
2561 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
2562 {
2563     ACImpl *This = impl_from_IAudioCaptureClient(iface);
2564     return IAudioClient_Release(&This->IAudioClient_iface);
2565 }
2566
2567 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
2568         BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
2569         UINT64 *qpcpos)
2570 {
2571     ACImpl *This = impl_from_IAudioCaptureClient(iface);
2572
2573     TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
2574             devpos, qpcpos);
2575
2576     if(!data || !frames || !flags)
2577         return E_POINTER;
2578
2579     EnterCriticalSection(&This->lock);
2580
2581     if(This->getbuf_last){
2582         LeaveCriticalSection(&This->lock);
2583         return AUDCLNT_E_OUT_OF_ORDER;
2584     }
2585
2586     /* hr = GetNextPacketSize(iface, frames); */
2587     if(This->held_frames < This->mmdev_period_frames){
2588         *frames = 0;
2589         LeaveCriticalSection(&This->lock);
2590         return AUDCLNT_S_BUFFER_EMPTY;
2591     }
2592     *frames = This->mmdev_period_frames;
2593
2594     if(This->lcl_offs_frames + *frames > This->bufsize_frames){
2595         UINT32 chunk_bytes, offs_bytes, frames_bytes;
2596         if(This->tmp_buffer_frames < *frames){
2597             HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
2598             This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
2599                     *frames * This->fmt->nBlockAlign);
2600             if(!This->tmp_buffer){
2601                 LeaveCriticalSection(&This->lock);
2602                 return E_OUTOFMEMORY;
2603             }
2604             This->tmp_buffer_frames = *frames;
2605         }
2606
2607         *data = This->tmp_buffer;
2608         chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
2609             This->fmt->nBlockAlign;
2610         offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
2611         frames_bytes = *frames * This->fmt->nBlockAlign;
2612         memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
2613         memcpy(This->tmp_buffer + chunk_bytes, This->local_buffer,
2614                 frames_bytes - chunk_bytes);
2615     }else
2616         *data = This->local_buffer +
2617             This->lcl_offs_frames * This->fmt->nBlockAlign;
2618
2619     This->getbuf_last = *frames;
2620     *flags = 0;
2621
2622     if(devpos)
2623       *devpos = This->written_frames;
2624     if(qpcpos){ /* fixme: qpc of recording time */
2625         LARGE_INTEGER stamp, freq;
2626         QueryPerformanceCounter(&stamp);
2627         QueryPerformanceFrequency(&freq);
2628         *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2629     }
2630
2631     LeaveCriticalSection(&This->lock);
2632
2633     return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
2634 }
2635
2636 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
2637         IAudioCaptureClient *iface, UINT32 done)
2638 {
2639     ACImpl *This = impl_from_IAudioCaptureClient(iface);
2640
2641     TRACE("(%p)->(%u)\n", This, done);
2642
2643     EnterCriticalSection(&This->lock);
2644
2645     if(!done){
2646         This->getbuf_last = 0;
2647         LeaveCriticalSection(&This->lock);
2648         return S_OK;
2649     }
2650
2651     if(!This->getbuf_last){
2652         LeaveCriticalSection(&This->lock);
2653         return AUDCLNT_E_OUT_OF_ORDER;
2654     }
2655
2656     if(This->getbuf_last != done){
2657         LeaveCriticalSection(&This->lock);
2658         return AUDCLNT_E_INVALID_SIZE;
2659     }
2660
2661     This->written_frames += done;
2662     This->held_frames -= done;
2663     This->lcl_offs_frames += done;
2664     This->lcl_offs_frames %= This->bufsize_frames;
2665     This->getbuf_last = 0;
2666
2667     LeaveCriticalSection(&This->lock);
2668
2669     return S_OK;
2670 }
2671
2672 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
2673         IAudioCaptureClient *iface, UINT32 *frames)
2674 {
2675     ACImpl *This = impl_from_IAudioCaptureClient(iface);
2676
2677     TRACE("(%p)->(%p)\n", This, frames);
2678
2679     if(!frames)
2680         return E_POINTER;
2681
2682     EnterCriticalSection(&This->lock);
2683
2684     *frames = This->held_frames < This->mmdev_period_frames ? 0 : This->mmdev_period_frames;
2685
2686     LeaveCriticalSection(&This->lock);
2687
2688     return S_OK;
2689 }
2690
2691 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
2692 {
2693     AudioCaptureClient_QueryInterface,
2694     AudioCaptureClient_AddRef,
2695     AudioCaptureClient_Release,
2696     AudioCaptureClient_GetBuffer,
2697     AudioCaptureClient_ReleaseBuffer,
2698     AudioCaptureClient_GetNextPacketSize
2699 };
2700
2701 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
2702         REFIID riid, void **ppv)
2703 {
2704     ACImpl *This = impl_from_IAudioClock(iface);
2705
2706     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2707
2708     if(!ppv)
2709         return E_POINTER;
2710     *ppv = NULL;
2711
2712     if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
2713         *ppv = iface;
2714     else if(IsEqualIID(riid, &IID_IAudioClock2))
2715         *ppv = &This->IAudioClock2_iface;
2716     if(*ppv){
2717         IUnknown_AddRef((IUnknown*)*ppv);
2718         return S_OK;
2719     }
2720
2721     WARN("Unknown interface %s\n", debugstr_guid(riid));
2722     return E_NOINTERFACE;
2723 }
2724
2725 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
2726 {
2727     ACImpl *This = impl_from_IAudioClock(iface);
2728     return IAudioClient_AddRef(&This->IAudioClient_iface);
2729 }
2730
2731 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
2732 {
2733     ACImpl *This = impl_from_IAudioClock(iface);
2734     return IAudioClient_Release(&This->IAudioClient_iface);
2735 }
2736
2737 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
2738 {
2739     ACImpl *This = impl_from_IAudioClock(iface);
2740
2741     TRACE("(%p)->(%p)\n", This, freq);
2742
2743     *freq = This->fmt->nSamplesPerSec;
2744
2745     return S_OK;
2746 }
2747
2748 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
2749         UINT64 *qpctime)
2750 {
2751     ACImpl *This = impl_from_IAudioClock(iface);
2752     UINT64 written_frames, position;
2753     UINT32 held_frames;
2754     int err;
2755     snd_pcm_state_t alsa_state;
2756     snd_pcm_uframes_t avail_frames;
2757     snd_pcm_sframes_t delay_frames;
2758
2759     TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
2760
2761     if(!pos)
2762         return E_POINTER;
2763
2764     EnterCriticalSection(&This->lock);
2765
2766     /* call required to get accurate snd_pcm_state() */
2767     avail_frames = snd_pcm_avail_update(This->pcm_handle);
2768     alsa_state = snd_pcm_state(This->pcm_handle);
2769     written_frames = This->written_frames;
2770     held_frames = This->held_frames;
2771
2772     err = snd_pcm_delay(This->pcm_handle, &delay_frames);
2773     if(err < 0){
2774         /* old Pulse, shortly after start */
2775         WARN("snd_pcm_delay failed in state %u: %d (%s)\n", alsa_state, err, snd_strerror(err));
2776     }
2777
2778     if(This->dataflow == eRender){
2779         position = written_frames - held_frames; /* maximum */
2780         if(!This->started || alsa_state > SND_PCM_STATE_RUNNING)
2781             ; /* mmdevapi stopped or ALSA underrun: pretend everything was played */
2782         else if(err<0 || delay_frames > position - This->last_pos_frames)
2783             /* Pulse bug: past underrun, despite recovery, avail_frames & delay
2784              * may be larger than alsa_bufsize_frames, as if cumulating frames. */
2785             /* Pulse bug: EIO(-5) shortly after starting: nothing played */
2786             position = This->last_pos_frames;
2787         else if(delay_frames > 0)
2788             position -= delay_frames;
2789     }else
2790         position = written_frames + held_frames;
2791
2792     /* ensure monotic growth */
2793     This->last_pos_frames = position;
2794
2795     LeaveCriticalSection(&This->lock);
2796
2797     TRACE("frames written: %u, held: %u, avail: %ld, delay: %ld state %d, pos: %u\n",
2798           (UINT32)(written_frames%1000000000), held_frames,
2799           avail_frames, delay_frames, alsa_state, (UINT32)(position%1000000000));
2800     *pos = position;
2801
2802     if(qpctime){
2803         LARGE_INTEGER stamp, freq;
2804         QueryPerformanceCounter(&stamp);
2805         QueryPerformanceFrequency(&freq);
2806         *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
2807     }
2808
2809     return S_OK;
2810 }
2811
2812 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
2813         DWORD *chars)
2814 {
2815     ACImpl *This = impl_from_IAudioClock(iface);
2816
2817     TRACE("(%p)->(%p)\n", This, chars);
2818
2819     if(!chars)
2820         return E_POINTER;
2821
2822     *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
2823
2824     return S_OK;
2825 }
2826
2827 static const IAudioClockVtbl AudioClock_Vtbl =
2828 {
2829     AudioClock_QueryInterface,
2830     AudioClock_AddRef,
2831     AudioClock_Release,
2832     AudioClock_GetFrequency,
2833     AudioClock_GetPosition,
2834     AudioClock_GetCharacteristics
2835 };
2836
2837 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
2838         REFIID riid, void **ppv)
2839 {
2840     ACImpl *This = impl_from_IAudioClock2(iface);
2841     return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
2842 }
2843
2844 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
2845 {
2846     ACImpl *This = impl_from_IAudioClock2(iface);
2847     return IAudioClient_AddRef(&This->IAudioClient_iface);
2848 }
2849
2850 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
2851 {
2852     ACImpl *This = impl_from_IAudioClock2(iface);
2853     return IAudioClient_Release(&This->IAudioClient_iface);
2854 }
2855
2856 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
2857         UINT64 *pos, UINT64 *qpctime)
2858 {
2859     ACImpl *This = impl_from_IAudioClock2(iface);
2860
2861     FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
2862
2863     return E_NOTIMPL;
2864 }
2865
2866 static const IAudioClock2Vtbl AudioClock2_Vtbl =
2867 {
2868     AudioClock2_QueryInterface,
2869     AudioClock2_AddRef,
2870     AudioClock2_Release,
2871     AudioClock2_GetDevicePosition
2872 };
2873
2874 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
2875 {
2876     AudioSessionWrapper *ret;
2877
2878     ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
2879             sizeof(AudioSessionWrapper));
2880     if(!ret)
2881         return NULL;
2882
2883     ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
2884     ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
2885     ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
2886
2887     ret->ref = 1;
2888
2889     ret->client = client;
2890     if(client){
2891         ret->session = client->session;
2892         AudioClient_AddRef(&client->IAudioClient_iface);
2893     }
2894
2895     return ret;
2896 }
2897
2898 static HRESULT WINAPI AudioSessionControl_QueryInterface(
2899         IAudioSessionControl2 *iface, REFIID riid, void **ppv)
2900 {
2901     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2902
2903     if(!ppv)
2904         return E_POINTER;
2905     *ppv = NULL;
2906
2907     if(IsEqualIID(riid, &IID_IUnknown) ||
2908             IsEqualIID(riid, &IID_IAudioSessionControl) ||
2909             IsEqualIID(riid, &IID_IAudioSessionControl2))
2910         *ppv = iface;
2911     if(*ppv){
2912         IUnknown_AddRef((IUnknown*)*ppv);
2913         return S_OK;
2914     }
2915
2916     WARN("Unknown interface %s\n", debugstr_guid(riid));
2917     return E_NOINTERFACE;
2918 }
2919
2920 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
2921 {
2922     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2923     ULONG ref;
2924     ref = InterlockedIncrement(&This->ref);
2925     TRACE("(%p) Refcount now %u\n", This, ref);
2926     return ref;
2927 }
2928
2929 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
2930 {
2931     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2932     ULONG ref;
2933     ref = InterlockedDecrement(&This->ref);
2934     TRACE("(%p) Refcount now %u\n", This, ref);
2935     if(!ref){
2936         if(This->client){
2937             EnterCriticalSection(&This->client->lock);
2938             This->client->session_wrapper = NULL;
2939             LeaveCriticalSection(&This->client->lock);
2940             AudioClient_Release(&This->client->IAudioClient_iface);
2941         }
2942         HeapFree(GetProcessHeap(), 0, This);
2943     }
2944     return ref;
2945 }
2946
2947 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
2948         AudioSessionState *state)
2949 {
2950     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2951     ACImpl *client;
2952
2953     TRACE("(%p)->(%p)\n", This, state);
2954
2955     if(!state)
2956         return NULL_PTR_ERR;
2957
2958     EnterCriticalSection(&g_sessions_lock);
2959
2960     if(list_empty(&This->session->clients)){
2961         *state = AudioSessionStateExpired;
2962         LeaveCriticalSection(&g_sessions_lock);
2963         return S_OK;
2964     }
2965
2966     LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2967         EnterCriticalSection(&client->lock);
2968         if(client->started){
2969             *state = AudioSessionStateActive;
2970             LeaveCriticalSection(&client->lock);
2971             LeaveCriticalSection(&g_sessions_lock);
2972             return S_OK;
2973         }
2974         LeaveCriticalSection(&client->lock);
2975     }
2976
2977     LeaveCriticalSection(&g_sessions_lock);
2978
2979     *state = AudioSessionStateInactive;
2980
2981     return S_OK;
2982 }
2983
2984 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2985         IAudioSessionControl2 *iface, WCHAR **name)
2986 {
2987     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2988
2989     FIXME("(%p)->(%p) - stub\n", This, name);
2990
2991     return E_NOTIMPL;
2992 }
2993
2994 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2995         IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2996 {
2997     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2998
2999     FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
3000
3001     return E_NOTIMPL;
3002 }
3003
3004 static HRESULT WINAPI AudioSessionControl_GetIconPath(
3005         IAudioSessionControl2 *iface, WCHAR **path)
3006 {
3007     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3008
3009     FIXME("(%p)->(%p) - stub\n", This, path);
3010
3011     return E_NOTIMPL;
3012 }
3013
3014 static HRESULT WINAPI AudioSessionControl_SetIconPath(
3015         IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
3016 {
3017     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3018
3019     FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
3020
3021     return E_NOTIMPL;
3022 }
3023
3024 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
3025         IAudioSessionControl2 *iface, GUID *group)
3026 {
3027     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3028
3029     FIXME("(%p)->(%p) - stub\n", This, group);
3030
3031     return E_NOTIMPL;
3032 }
3033
3034 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
3035         IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
3036 {
3037     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3038
3039     FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
3040             debugstr_guid(session));
3041
3042     return E_NOTIMPL;
3043 }
3044
3045 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
3046         IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3047 {
3048     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3049
3050     FIXME("(%p)->(%p) - stub\n", This, events);
3051
3052     return S_OK;
3053 }
3054
3055 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
3056         IAudioSessionControl2 *iface, IAudioSessionEvents *events)
3057 {
3058     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3059
3060     FIXME("(%p)->(%p) - stub\n", This, events);
3061
3062     return S_OK;
3063 }
3064
3065 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
3066         IAudioSessionControl2 *iface, WCHAR **id)
3067 {
3068     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3069
3070     FIXME("(%p)->(%p) - stub\n", This, id);
3071
3072     return E_NOTIMPL;
3073 }
3074
3075 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
3076         IAudioSessionControl2 *iface, WCHAR **id)
3077 {
3078     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3079
3080     FIXME("(%p)->(%p) - stub\n", This, id);
3081
3082     return E_NOTIMPL;
3083 }
3084
3085 static HRESULT WINAPI AudioSessionControl_GetProcessId(
3086         IAudioSessionControl2 *iface, DWORD *pid)
3087 {
3088     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3089
3090     TRACE("(%p)->(%p)\n", This, pid);
3091
3092     if(!pid)
3093         return E_POINTER;
3094
3095     *pid = GetCurrentProcessId();
3096
3097     return S_OK;
3098 }
3099
3100 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
3101         IAudioSessionControl2 *iface)
3102 {
3103     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3104
3105     TRACE("(%p)\n", This);
3106
3107     return S_FALSE;
3108 }
3109
3110 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
3111         IAudioSessionControl2 *iface, BOOL optout)
3112 {
3113     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
3114
3115     TRACE("(%p)->(%d)\n", This, optout);
3116
3117     return S_OK;
3118 }
3119
3120 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
3121 {
3122     AudioSessionControl_QueryInterface,
3123     AudioSessionControl_AddRef,
3124     AudioSessionControl_Release,
3125     AudioSessionControl_GetState,
3126     AudioSessionControl_GetDisplayName,
3127     AudioSessionControl_SetDisplayName,
3128     AudioSessionControl_GetIconPath,
3129     AudioSessionControl_SetIconPath,
3130     AudioSessionControl_GetGroupingParam,
3131     AudioSessionControl_SetGroupingParam,
3132     AudioSessionControl_RegisterAudioSessionNotification,
3133     AudioSessionControl_UnregisterAudioSessionNotification,
3134     AudioSessionControl_GetSessionIdentifier,
3135     AudioSessionControl_GetSessionInstanceIdentifier,
3136     AudioSessionControl_GetProcessId,
3137     AudioSessionControl_IsSystemSoundsSession,
3138     AudioSessionControl_SetDuckingPreference
3139 };
3140
3141 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
3142         ISimpleAudioVolume *iface, REFIID riid, void **ppv)
3143 {
3144     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3145
3146     if(!ppv)
3147         return E_POINTER;
3148     *ppv = NULL;
3149
3150     if(IsEqualIID(riid, &IID_IUnknown) ||
3151             IsEqualIID(riid, &IID_ISimpleAudioVolume))
3152         *ppv = iface;
3153     if(*ppv){
3154         IUnknown_AddRef((IUnknown*)*ppv);
3155         return S_OK;
3156     }
3157
3158     WARN("Unknown interface %s\n", debugstr_guid(riid));
3159     return E_NOINTERFACE;
3160 }
3161
3162 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
3163 {
3164     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3165     return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3166 }
3167
3168 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
3169 {
3170     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3171     return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3172 }
3173
3174 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
3175         ISimpleAudioVolume *iface, float level, const GUID *context)
3176 {
3177     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3178     AudioSession *session = This->session;
3179
3180     TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
3181
3182     if(level < 0.f || level > 1.f)
3183         return E_INVALIDARG;
3184
3185     if(context)
3186         FIXME("Notifications not supported yet\n");
3187
3188     TRACE("ALSA does not support volume control\n");
3189
3190     EnterCriticalSection(&session->lock);
3191
3192     session->master_vol = level;
3193
3194     LeaveCriticalSection(&session->lock);
3195
3196     return S_OK;
3197 }
3198
3199 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
3200         ISimpleAudioVolume *iface, float *level)
3201 {
3202     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3203     AudioSession *session = This->session;
3204
3205     TRACE("(%p)->(%p)\n", session, level);
3206
3207     if(!level)
3208         return NULL_PTR_ERR;
3209
3210     *level = session->master_vol;
3211
3212     return S_OK;
3213 }
3214
3215 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
3216         BOOL mute, const GUID *context)
3217 {
3218     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3219     AudioSession *session = This->session;
3220
3221     TRACE("(%p)->(%u, %p)\n", session, mute, context);
3222
3223     if(context)
3224         FIXME("Notifications not supported yet\n");
3225
3226     session->mute = mute;
3227
3228     return S_OK;
3229 }
3230
3231 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
3232         BOOL *mute)
3233 {
3234     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
3235     AudioSession *session = This->session;
3236
3237     TRACE("(%p)->(%p)\n", session, mute);
3238
3239     if(!mute)
3240         return NULL_PTR_ERR;
3241
3242     *mute = session->mute;
3243
3244     return S_OK;
3245 }
3246
3247 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl  =
3248 {
3249     SimpleAudioVolume_QueryInterface,
3250     SimpleAudioVolume_AddRef,
3251     SimpleAudioVolume_Release,
3252     SimpleAudioVolume_SetMasterVolume,
3253     SimpleAudioVolume_GetMasterVolume,
3254     SimpleAudioVolume_SetMute,
3255     SimpleAudioVolume_GetMute
3256 };
3257
3258 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
3259         IAudioStreamVolume *iface, REFIID riid, void **ppv)
3260 {
3261     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3262
3263     if(!ppv)
3264         return E_POINTER;
3265     *ppv = NULL;
3266
3267     if(IsEqualIID(riid, &IID_IUnknown) ||
3268             IsEqualIID(riid, &IID_IAudioStreamVolume))
3269         *ppv = iface;
3270     if(*ppv){
3271         IUnknown_AddRef((IUnknown*)*ppv);
3272         return S_OK;
3273     }
3274
3275     WARN("Unknown interface %s\n", debugstr_guid(riid));
3276     return E_NOINTERFACE;
3277 }
3278
3279 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
3280 {
3281     ACImpl *This = impl_from_IAudioStreamVolume(iface);
3282     return IAudioClient_AddRef(&This->IAudioClient_iface);
3283 }
3284
3285 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
3286 {
3287     ACImpl *This = impl_from_IAudioStreamVolume(iface);
3288     return IAudioClient_Release(&This->IAudioClient_iface);
3289 }
3290
3291 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
3292         IAudioStreamVolume *iface, UINT32 *out)
3293 {
3294     ACImpl *This = impl_from_IAudioStreamVolume(iface);
3295
3296     TRACE("(%p)->(%p)\n", This, out);
3297
3298     if(!out)
3299         return E_POINTER;
3300
3301     *out = This->fmt->nChannels;
3302
3303     return S_OK;
3304 }
3305
3306 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
3307         IAudioStreamVolume *iface, UINT32 index, float level)
3308 {
3309     ACImpl *This = impl_from_IAudioStreamVolume(iface);
3310
3311     TRACE("(%p)->(%d, %f)\n", This, index, level);
3312
3313     if(level < 0.f || level > 1.f)
3314         return E_INVALIDARG;
3315
3316     if(index >= This->fmt->nChannels)
3317         return E_INVALIDARG;
3318
3319     TRACE("ALSA does not support volume control\n");
3320
3321     EnterCriticalSection(&This->lock);
3322
3323     This->vols[index] = level;
3324
3325     LeaveCriticalSection(&This->lock);
3326
3327     return S_OK;
3328 }
3329
3330 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
3331         IAudioStreamVolume *iface, UINT32 index, float *level)
3332 {
3333     ACImpl *This = impl_from_IAudioStreamVolume(iface);
3334
3335     TRACE("(%p)->(%d, %p)\n", This, index, level);
3336
3337     if(!level)
3338         return E_POINTER;
3339
3340     if(index >= This->fmt->nChannels)
3341         return E_INVALIDARG;
3342
3343     *level = This->vols[index];
3344
3345     return S_OK;
3346 }
3347
3348 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
3349         IAudioStreamVolume *iface, UINT32 count, const float *levels)
3350 {
3351     ACImpl *This = impl_from_IAudioStreamVolume(iface);
3352     int i;
3353
3354     TRACE("(%p)->(%d, %p)\n", This, count, levels);
3355
3356     if(!levels)
3357         return E_POINTER;
3358
3359     if(count != This->fmt->nChannels)
3360         return E_INVALIDARG;
3361
3362     TRACE("ALSA does not support volume control\n");
3363
3364     EnterCriticalSection(&This->lock);
3365
3366     for(i = 0; i < count; ++i)
3367         This->vols[i] = levels[i];
3368
3369     LeaveCriticalSection(&This->lock);
3370
3371     return S_OK;
3372 }
3373
3374 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
3375         IAudioStreamVolume *iface, UINT32 count, float *levels)
3376 {
3377     ACImpl *This = impl_from_IAudioStreamVolume(iface);
3378     int i;
3379
3380     TRACE("(%p)->(%d, %p)\n", This, count, levels);
3381
3382     if(!levels)
3383         return E_POINTER;
3384
3385     if(count != This->fmt->nChannels)
3386         return E_INVALIDARG;
3387
3388     EnterCriticalSection(&This->lock);
3389
3390     for(i = 0; i < count; ++i)
3391         levels[i] = This->vols[i];
3392
3393     LeaveCriticalSection(&This->lock);
3394
3395     return S_OK;
3396 }
3397
3398 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
3399 {
3400     AudioStreamVolume_QueryInterface,
3401     AudioStreamVolume_AddRef,
3402     AudioStreamVolume_Release,
3403     AudioStreamVolume_GetChannelCount,
3404     AudioStreamVolume_SetChannelVolume,
3405     AudioStreamVolume_GetChannelVolume,
3406     AudioStreamVolume_SetAllVolumes,
3407     AudioStreamVolume_GetAllVolumes
3408 };
3409
3410 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
3411         IChannelAudioVolume *iface, REFIID riid, void **ppv)
3412 {
3413     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3414
3415     if(!ppv)
3416         return E_POINTER;
3417     *ppv = NULL;
3418
3419     if(IsEqualIID(riid, &IID_IUnknown) ||
3420             IsEqualIID(riid, &IID_IChannelAudioVolume))
3421         *ppv = iface;
3422     if(*ppv){
3423         IUnknown_AddRef((IUnknown*)*ppv);
3424         return S_OK;
3425     }
3426
3427     WARN("Unknown interface %s\n", debugstr_guid(riid));
3428     return E_NOINTERFACE;
3429 }
3430
3431 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
3432 {
3433     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3434     return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
3435 }
3436
3437 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
3438 {
3439     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3440     return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
3441 }
3442
3443 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
3444         IChannelAudioVolume *iface, UINT32 *out)
3445 {
3446     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3447     AudioSession *session = This->session;
3448
3449     TRACE("(%p)->(%p)\n", session, out);
3450
3451     if(!out)
3452         return NULL_PTR_ERR;
3453
3454     *out = session->channel_count;
3455
3456     return S_OK;
3457 }
3458
3459 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
3460         IChannelAudioVolume *iface, UINT32 index, float level,
3461         const GUID *context)
3462 {
3463     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3464     AudioSession *session = This->session;
3465
3466     TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
3467             wine_dbgstr_guid(context));
3468
3469     if(level < 0.f || level > 1.f)
3470         return E_INVALIDARG;
3471
3472     if(index >= session->channel_count)
3473         return E_INVALIDARG;
3474
3475     if(context)
3476         FIXME("Notifications not supported yet\n");
3477
3478     TRACE("ALSA does not support volume control\n");
3479
3480     EnterCriticalSection(&session->lock);
3481
3482     session->channel_vols[index] = level;
3483
3484     LeaveCriticalSection(&session->lock);
3485
3486     return S_OK;
3487 }
3488
3489 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
3490         IChannelAudioVolume *iface, UINT32 index, float *level)
3491 {
3492     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3493     AudioSession *session = This->session;
3494
3495     TRACE("(%p)->(%d, %p)\n", session, index, level);
3496
3497     if(!level)
3498         return NULL_PTR_ERR;
3499
3500     if(index >= session->channel_count)
3501         return E_INVALIDARG;
3502
3503     *level = session->channel_vols[index];
3504
3505     return S_OK;
3506 }
3507
3508 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
3509         IChannelAudioVolume *iface, UINT32 count, const float *levels,
3510         const GUID *context)
3511 {
3512     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3513     AudioSession *session = This->session;
3514     int i;
3515
3516     TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
3517             wine_dbgstr_guid(context));
3518
3519     if(!levels)
3520         return NULL_PTR_ERR;
3521
3522     if(count != session->channel_count)
3523         return E_INVALIDARG;
3524
3525     if(context)
3526         FIXME("Notifications not supported yet\n");
3527
3528     TRACE("ALSA does not support volume control\n");
3529
3530     EnterCriticalSection(&session->lock);
3531
3532     for(i = 0; i < count; ++i)
3533         session->channel_vols[i] = levels[i];
3534
3535     LeaveCriticalSection(&session->lock);
3536
3537     return S_OK;
3538 }
3539
3540 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
3541         IChannelAudioVolume *iface, UINT32 count, float *levels)
3542 {
3543     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
3544     AudioSession *session = This->session;
3545     int i;
3546
3547     TRACE("(%p)->(%d, %p)\n", session, count, levels);
3548
3549     if(!levels)
3550         return NULL_PTR_ERR;
3551
3552     if(count != session->channel_count)
3553         return E_INVALIDARG;
3554
3555     for(i = 0; i < count; ++i)
3556         levels[i] = session->channel_vols[i];
3557
3558     return S_OK;
3559 }
3560
3561 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
3562 {
3563     ChannelAudioVolume_QueryInterface,
3564     ChannelAudioVolume_AddRef,
3565     ChannelAudioVolume_Release,
3566     ChannelAudioVolume_GetChannelCount,
3567     ChannelAudioVolume_SetChannelVolume,
3568     ChannelAudioVolume_GetChannelVolume,
3569     ChannelAudioVolume_SetAllVolumes,
3570     ChannelAudioVolume_GetAllVolumes
3571 };
3572
3573 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
3574         REFIID riid, void **ppv)
3575 {
3576     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
3577
3578     if(!ppv)
3579         return E_POINTER;
3580     *ppv = NULL;
3581
3582     if(IsEqualIID(riid, &IID_IUnknown) ||
3583             IsEqualIID(riid, &IID_IAudioSessionManager) ||
3584             IsEqualIID(riid, &IID_IAudioSessionManager2))
3585         *ppv = iface;
3586     if(*ppv){
3587         IUnknown_AddRef((IUnknown*)*ppv);
3588         return S_OK;
3589     }
3590
3591     WARN("Unknown interface %s\n", debugstr_guid(riid));
3592     return E_NOINTERFACE;
3593 }
3594
3595 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
3596 {
3597     SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3598     ULONG ref;
3599     ref = InterlockedIncrement(&This->ref);
3600     TRACE("(%p) Refcount now %u\n", This, ref);
3601     return ref;
3602 }
3603
3604 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
3605 {
3606     SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3607     ULONG ref;
3608     ref = InterlockedDecrement(&This->ref);
3609     TRACE("(%p) Refcount now %u\n", This, ref);
3610     if(!ref)
3611         HeapFree(GetProcessHeap(), 0, This);
3612     return ref;
3613 }
3614
3615 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
3616         IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3617         IAudioSessionControl **out)
3618 {
3619     SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3620     AudioSession *session;
3621     AudioSessionWrapper *wrapper;
3622     HRESULT hr;
3623
3624     TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3625             flags, out);
3626
3627     hr = get_audio_session(session_guid, This->device, 0, &session);
3628     if(FAILED(hr))
3629         return hr;
3630
3631     wrapper = AudioSessionWrapper_Create(NULL);
3632     if(!wrapper)
3633         return E_OUTOFMEMORY;
3634
3635     wrapper->session = session;
3636
3637     *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
3638
3639     return S_OK;
3640 }
3641
3642 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
3643         IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
3644         ISimpleAudioVolume **out)
3645 {
3646     SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3647     AudioSession *session;
3648     AudioSessionWrapper *wrapper;
3649     HRESULT hr;
3650
3651     TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
3652             flags, out);
3653
3654     hr = get_audio_session(session_guid, This->device, 0, &session);
3655     if(FAILED(hr))
3656         return hr;
3657
3658     wrapper = AudioSessionWrapper_Create(NULL);
3659     if(!wrapper)
3660         return E_OUTOFMEMORY;
3661
3662     wrapper->session = session;
3663
3664     *out = &wrapper->ISimpleAudioVolume_iface;
3665
3666     return S_OK;
3667 }
3668
3669 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
3670         IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
3671 {
3672     SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3673     FIXME("(%p)->(%p) - stub\n", This, out);
3674     return E_NOTIMPL;
3675 }
3676
3677 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
3678         IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3679 {
3680     SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3681     FIXME("(%p)->(%p) - stub\n", This, notification);
3682     return E_NOTIMPL;
3683 }
3684
3685 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
3686         IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
3687 {
3688     SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3689     FIXME("(%p)->(%p) - stub\n", This, notification);
3690     return E_NOTIMPL;
3691 }
3692
3693 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
3694         IAudioSessionManager2 *iface, const WCHAR *session_id,
3695         IAudioVolumeDuckNotification *notification)
3696 {
3697     SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3698     FIXME("(%p)->(%p) - stub\n", This, notification);
3699     return E_NOTIMPL;
3700 }
3701
3702 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
3703         IAudioSessionManager2 *iface,
3704         IAudioVolumeDuckNotification *notification)
3705 {
3706     SessionMgr *This = impl_from_IAudioSessionManager2(iface);
3707     FIXME("(%p)->(%p) - stub\n", This, notification);
3708     return E_NOTIMPL;
3709 }
3710
3711 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
3712 {
3713     AudioSessionManager_QueryInterface,
3714     AudioSessionManager_AddRef,
3715     AudioSessionManager_Release,
3716     AudioSessionManager_GetAudioSessionControl,
3717     AudioSessionManager_GetSimpleAudioVolume,
3718     AudioSessionManager_GetSessionEnumerator,
3719     AudioSessionManager_RegisterSessionNotification,
3720     AudioSessionManager_UnregisterSessionNotification,
3721     AudioSessionManager_RegisterDuckNotification,
3722     AudioSessionManager_UnregisterDuckNotification
3723 };
3724
3725 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
3726         IAudioSessionManager2 **out)
3727 {
3728     SessionMgr *This;
3729
3730     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
3731     if(!This)
3732         return E_OUTOFMEMORY;
3733
3734     This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
3735     This->device = device;
3736     This->ref = 1;
3737
3738     *out = &This->IAudioSessionManager2_iface;
3739
3740     return S_OK;
3741 }