mmdevapi/tests: Add tests for volume control interfaces.
[wine] / dlls / wineoss.drv / mmdevdrv.c
1 /*
2  * Copyright 2011 Andrew Eikum for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #define NONAMELESSUNION
20 #define COBJMACROS
21 #include "config.h"
22
23 #include <stdarg.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/ioctl.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <math.h>
35 #include <sys/soundcard.h>
36
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winreg.h"
41 #include "wine/debug.h"
42 #include "wine/unicode.h"
43 #include "wine/list.h"
44
45 #include "ole2.h"
46 #include "mmdeviceapi.h"
47 #include "devpkey.h"
48 #include "dshow.h"
49 #include "dsound.h"
50 #include "endpointvolume.h"
51
52 #include "initguid.h"
53 #include "audiopolicy.h"
54 #include "audioclient.h"
55
56 WINE_DEFAULT_DEBUG_CHANNEL(oss);
57
58 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
59
60 static const REFERENCE_TIME DefaultPeriod = 200000;
61 static const REFERENCE_TIME MinimumPeriod = 100000;
62
63 struct ACImpl;
64 typedef struct ACImpl ACImpl;
65
66 typedef struct _AudioSession {
67     GUID guid;
68     struct list clients;
69
70     EDataFlow dataflow;
71
72     float master_vol;
73     UINT32 channel_count;
74     float *channel_vols;
75
76     CRITICAL_SECTION lock;
77
78     struct list entry;
79 } AudioSession;
80
81 typedef struct _AudioSessionWrapper {
82     IAudioSessionControl2 IAudioSessionControl2_iface;
83     IChannelAudioVolume IChannelAudioVolume_iface;
84     ISimpleAudioVolume ISimpleAudioVolume_iface;
85
86     LONG ref;
87
88     ACImpl *client;
89     AudioSession *session;
90 } AudioSessionWrapper;
91
92 struct ACImpl {
93     IAudioClient IAudioClient_iface;
94     IAudioRenderClient IAudioRenderClient_iface;
95     IAudioCaptureClient IAudioCaptureClient_iface;
96     IAudioClock IAudioClock_iface;
97     IAudioClock2 IAudioClock2_iface;
98     IAudioStreamVolume IAudioStreamVolume_iface;
99
100     LONG ref;
101
102     IMMDevice *parent;
103
104     WAVEFORMATEX *fmt;
105
106     EDataFlow dataflow;
107     DWORD flags;
108     AUDCLNT_SHAREMODE share;
109     HANDLE event;
110     float *vols;
111
112     int fd;
113     oss_audioinfo ai;
114
115     BOOL initted, playing;
116     UINT64 written_frames, held_frames, tmp_buffer_frames, inbuf_frames;
117     UINT32 period_us, bufsize_frames;
118     UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
119
120     BYTE *local_buffer, *tmp_buffer;
121     int buf_state;
122     HANDLE timer;
123
124     CRITICAL_SECTION lock;
125
126     AudioSession *session;
127     AudioSessionWrapper *session_wrapper;
128
129     struct list entry;
130 };
131
132 enum BufferStates {
133     NOT_LOCKED = 0,
134     LOCKED_NORMAL, /* public buffer piece is from local_buffer */
135     LOCKED_WRAPPED /* public buffer piece is in tmp_buffer */
136 };
137
138 static HANDLE g_timer_q;
139
140 static CRITICAL_SECTION g_sessions_lock;
141 static struct list g_sessions = LIST_INIT(g_sessions);
142
143 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
144 static HRESULT oss_setvol(ACImpl *This, UINT32 index);
145
146 static const IAudioClientVtbl AudioClient_Vtbl;
147 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
148 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
149 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
150 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
151 static const IAudioClockVtbl AudioClock_Vtbl;
152 static const IAudioClock2Vtbl AudioClock2_Vtbl;
153 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
154 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
155
156 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
157 {
158     return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
159 }
160
161 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
162 {
163     return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
164 }
165
166 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
167 {
168     return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
169 }
170
171 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
172 {
173     return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
174 }
175
176 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
177 {
178     return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
179 }
180
181 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
182 {
183     return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
184 }
185
186 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
187 {
188     return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
189 }
190
191 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
192 {
193     return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
194 }
195
196 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
197 {
198     return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
199 }
200
201 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
202 {
203     if(reason == DLL_PROCESS_ATTACH){
204         g_timer_q = CreateTimerQueue();
205         if(!g_timer_q)
206             return FALSE;
207
208         InitializeCriticalSection(&g_sessions_lock);
209     }
210
211     return TRUE;
212 }
213
214 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys,
215         UINT *num, UINT *def_index)
216 {
217     int i, mixer_fd;
218     oss_sysinfo sysinfo;
219     static int print_once = 0;
220
221     TRACE("%d %p %p %p\n", flow, ids, num, def_index);
222
223     mixer_fd = open("/dev/mixer", O_RDONLY, 0);
224     if(mixer_fd < 0){
225         ERR("OSS /dev/mixer doesn't seem to exist\n");
226         return AUDCLNT_E_SERVICE_NOT_RUNNING;
227     }
228
229     if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
230         close(mixer_fd);
231
232         if(errno == EINVAL){
233             ERR("OSS version too old, need at least OSSv4\n");
234             return AUDCLNT_E_SERVICE_NOT_RUNNING;
235         }
236
237         ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
238         return E_FAIL;
239     }
240
241     if(!print_once){
242         TRACE("OSS sysinfo:\n");
243         TRACE("product: %s\n", sysinfo.product);
244         TRACE("version: %s\n", sysinfo.version);
245         TRACE("versionnum: %x\n", sysinfo.versionnum);
246         TRACE("numaudios: %d\n", sysinfo.numaudios);
247         TRACE("nummixers: %d\n", sysinfo.nummixers);
248         TRACE("numcards: %d\n", sysinfo.numcards);
249         TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
250         print_once = 1;
251     }
252
253     if(sysinfo.numaudios <= 0){
254         WARN("No audio devices!\n");
255         close(mixer_fd);
256         return AUDCLNT_E_SERVICE_NOT_RUNNING;
257     }
258
259     *ids = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(WCHAR *));
260     *keys = HeapAlloc(GetProcessHeap(), 0, sysinfo.numaudios * sizeof(char *));
261
262     *num = 0;
263     *def_index = -1;
264     for(i = 0; i < sysinfo.numaudios; ++i){
265         oss_audioinfo ai = {0};
266
267         ai.dev = i;
268         if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
269             WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
270                     strerror(errno));
271             continue;
272         }
273
274         if((flow == eCapture && (ai.caps & PCM_CAP_INPUT)) ||
275                 (flow == eRender && (ai.caps & PCM_CAP_OUTPUT))){
276             size_t len;
277
278             (*keys)[*num] = HeapAlloc(GetProcessHeap(), 0,
279                     strlen(ai.devnode) + 1);
280             if(!(*keys)[*num]){
281                 for(i = 0; i < *num; ++i){
282                     HeapFree(GetProcessHeap(), 0, (*ids)[i]);
283                     HeapFree(GetProcessHeap(), 0, (*keys)[i]);
284                 }
285                 HeapFree(GetProcessHeap(), 0, *ids);
286                 HeapFree(GetProcessHeap(), 0, *keys);
287                 close(mixer_fd);
288                 return E_OUTOFMEMORY;
289             }
290             strcpy((*keys)[*num], ai.devnode);
291
292             len = MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1, NULL, 0);
293             (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0,
294                     len * sizeof(WCHAR));
295             if(!(*ids)[*num]){
296                 HeapFree(GetProcessHeap(), 0, (*keys)[*num]);
297                 for(i = 0; i < *num; ++i){
298                     HeapFree(GetProcessHeap(), 0, (*ids)[i]);
299                     HeapFree(GetProcessHeap(), 0, (*keys)[i]);
300                 }
301                 HeapFree(GetProcessHeap(), 0, *ids);
302                 HeapFree(GetProcessHeap(), 0, *keys);
303                 close(mixer_fd);
304                 return E_OUTOFMEMORY;
305             }
306             MultiByteToWideChar(CP_UNIXCP, 0, ai.name, -1,
307                     (*ids)[*num], len);
308
309             if(ai.caps & PCM_CAP_DEFAULT)
310                 *def_index = *num;
311
312             (*num)++;
313         }
314     }
315
316     if(*def_index == -1)
317         *def_index = 0;
318
319     close(mixer_fd);
320
321     return S_OK;
322 }
323
324 HRESULT WINAPI AUDDRV_GetAudioEndpoint(char *devnode, IMMDevice *dev,
325         EDataFlow dataflow, IAudioClient **out)
326 {
327     ACImpl *This;
328
329     TRACE("%s %p %d %p\n", devnode, dev, dataflow, out);
330
331     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACImpl));
332     if(!This)
333         return E_OUTOFMEMORY;
334
335     if(dataflow == eRender)
336         This->fd = open(devnode, O_WRONLY, 0);
337     else if(dataflow == eCapture)
338         This->fd = open(devnode, O_RDONLY, 0);
339     else{
340         HeapFree(GetProcessHeap(), 0, This);
341         return E_INVALIDARG;
342     }
343     if(This->fd < 0){
344         ERR("Unable to open device %s: %d (%s)\n", devnode, errno,
345                 strerror(errno));
346         HeapFree(GetProcessHeap(), 0, This);
347         return AUDCLNT_E_DEVICE_INVALIDATED;
348     }
349
350     This->dataflow = dataflow;
351
352     This->ai.dev = -1;
353     if(ioctl(This->fd, SNDCTL_ENGINEINFO, &This->ai) < 0){
354         ERR("Unable to get audio info for device %s: %d (%s)\n", devnode,
355                 errno, strerror(errno));
356         close(This->fd);
357         HeapFree(GetProcessHeap(), 0, This);
358         return E_FAIL;
359     }
360
361     TRACE("OSS audioinfo:\n");
362     TRACE("devnode: %s\n", This->ai.devnode);
363     TRACE("name: %s\n", This->ai.name);
364     TRACE("busy: %x\n", This->ai.busy);
365     TRACE("caps: %x\n", This->ai.caps);
366     TRACE("iformats: %x\n", This->ai.iformats);
367     TRACE("oformats: %x\n", This->ai.oformats);
368     TRACE("enabled: %d\n", This->ai.enabled);
369     TRACE("min_rate: %d\n", This->ai.min_rate);
370     TRACE("max_rate: %d\n", This->ai.max_rate);
371     TRACE("min_channels: %d\n", This->ai.min_channels);
372     TRACE("max_channels: %d\n", This->ai.max_channels);
373
374     This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
375     This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
376     This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
377     This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
378     This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
379     This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
380
381     InitializeCriticalSection(&This->lock);
382
383     This->parent = dev;
384     IMMDevice_AddRef(This->parent);
385
386     IAudioClient_AddRef(&This->IAudioClient_iface);
387
388     *out = &This->IAudioClient_iface;
389
390     return S_OK;
391 }
392
393 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
394         REFIID riid, void **ppv)
395 {
396     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
397
398     if(!ppv)
399         return E_POINTER;
400     *ppv = NULL;
401     if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
402         *ppv = iface;
403     if(*ppv){
404         IUnknown_AddRef((IUnknown*)*ppv);
405         return S_OK;
406     }
407     WARN("Unknown interface %s\n", debugstr_guid(riid));
408     return E_NOINTERFACE;
409 }
410
411 static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
412 {
413     ACImpl *This = impl_from_IAudioClient(iface);
414     ULONG ref;
415     ref = InterlockedIncrement(&This->ref);
416     TRACE("(%p) Refcount now %u\n", This, ref);
417     return ref;
418 }
419
420 static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
421 {
422     ACImpl *This = impl_from_IAudioClient(iface);
423     ULONG ref;
424     ref = InterlockedDecrement(&This->ref);
425     TRACE("(%p) Refcount now %u\n", This, ref);
426     if(!ref){
427         IAudioClient_Stop(iface);
428         IMMDevice_Release(This->parent);
429         DeleteCriticalSection(&This->lock);
430         close(This->fd);
431         if(This->initted){
432             EnterCriticalSection(&g_sessions_lock);
433             list_remove(&This->entry);
434             if(list_empty(&This->session->clients)){
435                 list_remove(&This->session->entry);
436                 DeleteCriticalSection(&This->session->lock);
437                 HeapFree(GetProcessHeap(), 0, This->session->channel_vols);
438                 HeapFree(GetProcessHeap(), 0, This->session);
439             }
440             LeaveCriticalSection(&g_sessions_lock);
441         }
442         HeapFree(GetProcessHeap(), 0, This->vols);
443         HeapFree(GetProcessHeap(), 0, This->local_buffer);
444         HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
445         CoTaskMemFree(This->fmt);
446         HeapFree(GetProcessHeap(), 0, This);
447     }
448     return ref;
449 }
450
451 static void dump_fmt(const WAVEFORMATEX *fmt)
452 {
453     TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
454     switch(fmt->wFormatTag){
455     case WAVE_FORMAT_PCM:
456         TRACE("WAVE_FORMAT_PCM");
457         break;
458     case WAVE_FORMAT_IEEE_FLOAT:
459         TRACE("WAVE_FORMAT_IEEE_FLOAT");
460         break;
461     case WAVE_FORMAT_EXTENSIBLE:
462         TRACE("WAVE_FORMAT_EXTENSIBLE");
463         break;
464     default:
465         TRACE("Unknown");
466         break;
467     }
468     TRACE(")\n");
469
470     TRACE("nChannels: %u\n", fmt->nChannels);
471     TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
472     TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
473     TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
474     TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
475     TRACE("cbSize: %u\n", fmt->cbSize);
476
477     if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
478         WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
479         TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
480         TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
481         TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
482     }
483 }
484
485 static DWORD get_channel_mask(unsigned int channels)
486 {
487     switch(channels){
488     case 0:
489         return 0;
490     case 1:
491         return SPEAKER_FRONT_CENTER;
492     case 2:
493         return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
494     case 3:
495         return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
496             SPEAKER_LOW_FREQUENCY;
497     case 4:
498         return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
499             SPEAKER_BACK_RIGHT;
500     case 5:
501         return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
502             SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
503     case 6:
504         return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
505             SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER;
506     case 7:
507         return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
508             SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
509             SPEAKER_BACK_CENTER;
510     }
511     FIXME("Unknown speaker configuration: %u\n", channels);
512     return 0;
513 }
514
515 static int get_oss_format(const WAVEFORMATEX *fmt)
516 {
517     WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
518
519     if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
520             (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
521              IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
522         switch(fmt->wBitsPerSample){
523         case 8:
524             return AFMT_U8;
525         case 16:
526             return AFMT_S16_LE;
527         case 24:
528             return AFMT_S24_LE;
529         case 32:
530             return AFMT_S32_LE;
531         }
532         return -1;
533     }
534
535 #ifdef AFMT_FLOAT
536     if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
537             (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
538              IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
539         if(fmt->wBitsPerSample != 32)
540             return -1;
541
542         return AFMT_FLOAT;
543     }
544 #endif
545
546     return -1;
547 }
548
549 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
550 {
551     WAVEFORMATEX *ret;
552     size_t size;
553
554     if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
555         size = sizeof(WAVEFORMATEXTENSIBLE);
556     else
557         size = sizeof(WAVEFORMATEX);
558
559     ret = CoTaskMemAlloc(size);
560     if(!ret)
561         return NULL;
562
563     memcpy(ret, fmt, size);
564
565     ret->cbSize = size - sizeof(WAVEFORMATEX);
566
567     return ret;
568 }
569
570 static HRESULT setup_oss_device(ACImpl *This, const WAVEFORMATEX *fmt,
571         WAVEFORMATEX **out)
572 {
573     int tmp, oss_format;
574     double tenth;
575     HRESULT ret = S_OK;
576     WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
577     WAVEFORMATEX *closest = NULL;
578
579     tmp = oss_format = get_oss_format(fmt);
580     if(oss_format < 0)
581         return AUDCLNT_E_UNSUPPORTED_FORMAT;
582     if(ioctl(This->fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
583         WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
584         return E_FAIL;
585     }
586     if(tmp != oss_format){
587         TRACE("Format unsupported by this OSS version: %x\n", oss_format);
588         return AUDCLNT_E_UNSUPPORTED_FORMAT;
589     }
590
591     closest = clone_format(fmt);
592
593     tmp = fmt->nSamplesPerSec;
594     if(ioctl(This->fd, SNDCTL_DSP_SPEED, &tmp) < 0){
595         WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
596         CoTaskMemFree(closest);
597         return E_FAIL;
598     }
599     tenth = fmt->nSamplesPerSec * 0.1;
600     if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
601         ret = S_FALSE;
602         closest->nSamplesPerSec = tmp;
603     }
604
605     tmp = fmt->nChannels;
606     if(ioctl(This->fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
607         WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
608         CoTaskMemFree(closest);
609         return E_FAIL;
610     }
611     if(tmp != fmt->nChannels){
612         ret = S_FALSE;
613         closest->nChannels = tmp;
614     }
615
616     if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
617         DWORD mask = get_channel_mask(closest->nChannels);
618
619         ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = mask;
620
621         if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
622                 fmtex->dwChannelMask != mask)
623             ret = S_FALSE;
624     }
625
626     if(ret == S_OK || !out){
627         CoTaskMemFree( closest);
628         if(out)
629             *out = NULL;
630     }else{
631         closest->nBlockAlign =
632             closest->nChannels * closest->wBitsPerSample / 8;
633         closest->nAvgBytesPerSec =
634             closest->nBlockAlign * closest->nSamplesPerSec;
635         *out = closest;
636     }
637
638     TRACE("returning: %08x\n", ret);
639     return ret;
640 }
641
642 static AudioSession *create_session(const GUID *guid, EDataFlow flow,
643         int num_channels)
644 {
645     AudioSession *ret;
646
647     ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
648     if(!ret)
649         return NULL;
650
651     memcpy(&ret->guid, guid, sizeof(GUID));
652
653     ret->dataflow = flow;
654
655     list_init(&ret->clients);
656
657     list_add_head(&g_sessions, &ret->entry);
658
659     InitializeCriticalSection(&ret->lock);
660
661     ret->channel_count = num_channels;
662     ret->channel_vols = HeapAlloc(GetProcessHeap(), 0,
663             sizeof(float) * num_channels);
664     if(!ret->channel_vols){
665         HeapFree(GetProcessHeap(), 0, ret);
666         return NULL;
667     }
668
669     for(; num_channels > 0; --num_channels)
670         ret->channel_vols[num_channels - 1] = 1.f;
671
672     ret->master_vol = 1.f;
673
674     return ret;
675 }
676
677 static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
678         AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
679         REFERENCE_TIME period, const WAVEFORMATEX *fmt,
680         const GUID *sessionguid)
681 {
682     ACImpl *This = impl_from_IAudioClient(iface);
683     int mask, i;
684     HRESULT hr;
685
686     TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
687           wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
688
689     if(!fmt)
690         return E_POINTER;
691
692     dump_fmt(fmt);
693
694     if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
695         return AUDCLNT_E_NOT_INITIALIZED;
696
697     if(flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
698                 AUDCLNT_STREAMFLAGS_LOOPBACK |
699                 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
700                 AUDCLNT_STREAMFLAGS_NOPERSIST |
701                 AUDCLNT_STREAMFLAGS_RATEADJUST |
702                 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
703                 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
704                 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)){
705         TRACE("Unknown flags: %08x\n", flags);
706         return E_INVALIDARG;
707     }
708
709     EnterCriticalSection(&This->lock);
710
711     if(This->initted){
712         LeaveCriticalSection(&This->lock);
713         return AUDCLNT_E_ALREADY_INITIALIZED;
714     }
715
716     hr = setup_oss_device(This, fmt, NULL);
717     if(hr == S_FALSE){
718         LeaveCriticalSection(&This->lock);
719         return AUDCLNT_E_UNSUPPORTED_FORMAT;
720     }
721     if(FAILED(hr)){
722         LeaveCriticalSection(&This->lock);
723         return hr;
724     }
725
726     mask = 0;
727     if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
728         LeaveCriticalSection(&This->lock);
729         WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
730         return E_FAIL;
731     }
732
733     mask = (100 << 8) | 100;
734     if(ioctl(This->fd, SNDCTL_DSP_SETPLAYVOL, &mask) < 0)
735         WARN("SETPLAYVOL failed: %d (%s)\n", errno, strerror(errno));
736
737     This->fmt = clone_format(fmt);
738     if(!This->fmt){
739         LeaveCriticalSection(&This->lock);
740         return E_OUTOFMEMORY;
741     }
742
743     if(period)
744         This->period_us = period / 10;
745     else
746         This->period_us = DefaultPeriod / 10;
747
748     This->bufsize_frames = ceil(fmt->nSamplesPerSec * (duration / 10000000.));
749     This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
750             This->bufsize_frames * fmt->nBlockAlign);
751     if(!This->local_buffer){
752         CoTaskMemFree(This->fmt);
753         This->fmt = NULL;
754         LeaveCriticalSection(&This->lock);
755         return E_OUTOFMEMORY;
756     }
757
758     This->vols = HeapAlloc(GetProcessHeap(), 0, fmt->nChannels * sizeof(float));
759     if(!This->vols){
760         CoTaskMemFree(This->fmt);
761         This->fmt = NULL;
762         LeaveCriticalSection(&This->lock);
763         return E_OUTOFMEMORY;
764     }
765
766     for(i = 0; i < fmt->nChannels; ++i)
767         This->vols[i] = 1.f;
768
769     This->share = mode;
770     This->flags = flags;
771
772     EnterCriticalSection(&g_sessions_lock);
773
774     if(!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)){
775         This->session = create_session(&GUID_NULL, This->dataflow,
776                 fmt->nChannels);
777         if(!This->session){
778             LeaveCriticalSection(&g_sessions_lock);
779             HeapFree(GetProcessHeap(), 0, This->vols);
780             This->vols = NULL;
781             CoTaskMemFree(This->fmt);
782             This->fmt = NULL;
783             LeaveCriticalSection(&This->lock);
784             return E_OUTOFMEMORY;
785         }
786     }else{
787         AudioSession *session;
788
789         LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry){
790             if(IsEqualGUID(sessionguid, &session->guid) &&
791                     This->dataflow == session->dataflow){
792                 if(session->channel_count != fmt->nChannels){
793                     LeaveCriticalSection(&g_sessions_lock);
794                     HeapFree(GetProcessHeap(), 0, This->vols);
795                     This->vols = NULL;
796                     CoTaskMemFree(This->fmt);
797                     This->fmt = NULL;
798                     LeaveCriticalSection(&This->lock);
799                     return E_INVALIDARG;
800                 }
801                 This->session = session;
802             }
803         }
804
805         if(!This->session){
806             This->session = create_session(sessionguid, This->dataflow,
807                     fmt->nChannels);
808             if(!This->session){
809                 LeaveCriticalSection(&g_sessions_lock);
810                 HeapFree(GetProcessHeap(), 0, This->vols);
811                 This->vols = NULL;
812                 CoTaskMemFree(This->fmt);
813                 This->fmt = NULL;
814                 LeaveCriticalSection(&This->lock);
815                 return E_OUTOFMEMORY;
816             }
817         }
818     }
819
820     list_add_tail(&This->session->clients, &This->entry);
821
822     LeaveCriticalSection(&g_sessions_lock);
823
824     This->initted = TRUE;
825
826     oss_setvol(This, -1);
827
828     LeaveCriticalSection(&This->lock);
829
830     return S_OK;
831 }
832
833 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
834         UINT32 *frames)
835 {
836     ACImpl *This = impl_from_IAudioClient(iface);
837
838     TRACE("(%p)->(%p)\n", This, frames);
839
840     if(!frames)
841         return E_POINTER;
842
843     EnterCriticalSection(&This->lock);
844
845     if(!This->initted){
846         LeaveCriticalSection(&This->lock);
847         return AUDCLNT_E_NOT_INITIALIZED;
848     }
849
850     *frames = This->bufsize_frames;
851
852     LeaveCriticalSection(&This->lock);
853
854     return S_OK;
855 }
856
857 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
858         REFERENCE_TIME *latency)
859 {
860     ACImpl *This = impl_from_IAudioClient(iface);
861
862     TRACE("(%p)->(%p)\n", This, latency);
863
864     if(!latency)
865         return E_POINTER;
866
867     EnterCriticalSection(&This->lock);
868
869     if(!This->initted){
870         LeaveCriticalSection(&This->lock);
871         return AUDCLNT_E_NOT_INITIALIZED;
872     }
873
874     if(This->dataflow == eRender){
875         int delay_bytes;
876         double delay_s;
877
878         if(ioctl(This->fd, SNDCTL_DSP_GETODELAY, &delay_bytes) < 0){
879             LeaveCriticalSection(&This->lock);
880             WARN("GETODELAY failed: %d (%s)\n", errno, strerror(errno));
881             return E_FAIL;
882         }
883
884         delay_s = delay_bytes / (double)(This->fmt->nSamplesPerSec *
885                 This->fmt->nBlockAlign);
886
887         *latency = delay_s * 10000000;
888     }else
889         *latency = 10000; /* OSS doesn't provide input latency */
890
891     LeaveCriticalSection(&This->lock);
892
893     return S_OK;
894 }
895
896 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
897         UINT32 *numpad)
898 {
899     ACImpl *This = impl_from_IAudioClient(iface);
900     audio_buf_info bi;
901
902     TRACE("(%p)->(%p)\n", This, numpad);
903
904     if(!numpad)
905         return E_POINTER;
906
907     EnterCriticalSection(&This->lock);
908
909     if(!This->initted){
910         LeaveCriticalSection(&This->lock);
911         return AUDCLNT_E_NOT_INITIALIZED;
912     }
913
914     if(This->dataflow == eRender){
915         if(ioctl(This->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
916             LeaveCriticalSection(&This->lock);
917             WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
918             return E_FAIL;
919         }
920
921         *numpad = (bi.fragstotal * bi.fragsize - bi.bytes) /
922             This->fmt->nBlockAlign;
923
924         /* when the OSS buffer has less than one fragment of data, including
925          * no data, it often reports it as some non-zero portion of a
926          * fragment. when it has more than one fragment of data, it reports
927          * it as some multiple of that portion of the fragment size.
928          *
929          * so, we have to do some ugly workarounds to report the timing
930          * as accurately as possible */
931         if(*numpad < bi.fragsize / This->fmt->nBlockAlign){
932             *numpad = This->inbuf_frames;
933             This->inbuf_frames = 0;
934         }else{
935             if(*numpad < This->inbuf_frames)
936                 This->inbuf_frames = *numpad;
937             else
938                 *numpad = This->inbuf_frames;
939         }
940     }else if(This->dataflow == eCapture){
941         if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
942             LeaveCriticalSection(&This->lock);
943             WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
944             return E_FAIL;
945         }
946
947         if(bi.bytes <= bi.fragsize)
948             *numpad = 0;
949         else
950             *numpad = bi.bytes / This->fmt->nBlockAlign;
951     }else{
952         LeaveCriticalSection(&This->lock);
953         return E_UNEXPECTED;
954     }
955
956     *numpad += This->held_frames;
957
958     LeaveCriticalSection(&This->lock);
959
960     return S_OK;
961 }
962
963 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
964         AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx,
965         WAVEFORMATEX **outpwfx)
966 {
967     ACImpl *This = impl_from_IAudioClient(iface);
968     HRESULT ret;
969
970     TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx);
971
972     if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx))
973         return E_POINTER;
974
975     if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
976         return E_INVALIDARG;
977
978     if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
979             pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
980         return E_INVALIDARG;
981
982     dump_fmt(pwfx);
983
984     EnterCriticalSection(&This->lock);
985
986     ret = setup_oss_device(This, pwfx, outpwfx);
987
988     LeaveCriticalSection(&This->lock);
989
990     return ret;
991 }
992
993 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
994         WAVEFORMATEX **pwfx)
995 {
996     ACImpl *This = impl_from_IAudioClient(iface);
997     WAVEFORMATEXTENSIBLE *fmt;
998     int formats;
999
1000     TRACE("(%p)->(%p)\n", This, pwfx);
1001
1002     if(!pwfx)
1003         return E_POINTER;
1004
1005     *pwfx = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE));
1006     if(!*pwfx)
1007         return E_OUTOFMEMORY;
1008
1009     fmt = (WAVEFORMATEXTENSIBLE*)*pwfx;
1010
1011     if(This->dataflow == eRender)
1012         formats = This->ai.oformats;
1013     else if(This->dataflow == eCapture)
1014         formats = This->ai.iformats;
1015     else
1016         return E_UNEXPECTED;
1017
1018     fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1019     if(formats & AFMT_S16_LE){
1020         fmt->Format.wBitsPerSample = 16;
1021         fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1022 #ifdef AFMT_FLOAT
1023     }else if(formats & AFMT_FLOAT){
1024         fmt->Format.wBitsPerSample = 32;
1025         fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1026 #endif
1027     }else if(formats & AFMT_U8){
1028         fmt->Format.wBitsPerSample = 8;
1029         fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1030     }else if(formats & AFMT_S32_LE){
1031         fmt->Format.wBitsPerSample = 32;
1032         fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1033     }else if(formats & AFMT_S24_LE){
1034         fmt->Format.wBitsPerSample = 24;
1035         fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1036     }else{
1037         ERR("Didn't recognize any available OSS formats: %x\n", formats);
1038         return E_FAIL;
1039     }
1040
1041     fmt->Format.nChannels = This->ai.max_channels;
1042     fmt->Format.nSamplesPerSec = This->ai.max_rate;
1043     fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1044
1045     fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1046             fmt->Format.nChannels) / 8;
1047     fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1048         fmt->Format.nBlockAlign;
1049
1050     fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1051     fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1052
1053     dump_fmt(*pwfx);
1054
1055     return S_OK;
1056 }
1057
1058 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
1059         REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1060 {
1061     ACImpl *This = impl_from_IAudioClient(iface);
1062
1063     TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1064
1065     if(!defperiod && !minperiod)
1066         return E_POINTER;
1067
1068     EnterCriticalSection(&This->lock);
1069
1070     if(defperiod)
1071         *defperiod = DefaultPeriod;
1072     if(minperiod)
1073         *minperiod = MinimumPeriod;
1074
1075     LeaveCriticalSection(&This->lock);
1076
1077     return S_OK;
1078 }
1079
1080 static void oss_write_data(ACImpl *This)
1081 {
1082     ssize_t written;
1083     UINT32 written_frames;
1084     size_t to_write;
1085     BYTE *buf =
1086         This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
1087
1088     if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
1089         to_write = This->bufsize_frames - This->lcl_offs_frames;
1090     else
1091         to_write = This->held_frames;
1092
1093     written = write(This->fd, buf, to_write * This->fmt->nBlockAlign);
1094     if(written < 0){
1095         WARN("write failed: %d (%s)\n", errno, strerror(errno));
1096         return;
1097     }
1098     written_frames = written / This->fmt->nBlockAlign;
1099
1100     This->lcl_offs_frames += written_frames;
1101     This->lcl_offs_frames %= This->bufsize_frames;
1102     This->held_frames -= written_frames;
1103     This->inbuf_frames += written_frames;
1104
1105     if(written_frames < to_write){
1106         /* OSS buffer probably full */
1107         return;
1108     }
1109
1110     if(This->held_frames){
1111         /* wrapped and have some data back at the start to write */
1112         written = write(This->fd, This->local_buffer,
1113                 This->held_frames * This->fmt->nBlockAlign);
1114         if(written < 0){
1115             WARN("write failed: %d (%s)\n", errno, strerror(errno));
1116             return;
1117         }
1118         written_frames = written / This->fmt->nBlockAlign;
1119
1120         This->lcl_offs_frames += written_frames;
1121         This->lcl_offs_frames %= This->bufsize_frames;
1122         This->held_frames -= written_frames;
1123         This->inbuf_frames += written_frames;
1124     }
1125 }
1126
1127 static void oss_read_data(ACImpl *This)
1128 {
1129     UINT64 pos, readable;
1130     audio_buf_info bi;
1131     ssize_t nread;
1132
1133     if(ioctl(This->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1134         WARN("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1135         return;
1136     }
1137
1138     pos = (This->held_frames + This->lcl_offs_frames) % This->bufsize_frames;
1139     readable = (This->bufsize_frames - pos) * This->fmt->nBlockAlign;
1140
1141     if(bi.bytes < readable)
1142         readable = bi.bytes;
1143
1144     nread = read(This->fd, This->local_buffer + pos * This->fmt->nBlockAlign,
1145             readable);
1146     if(nread < 0){
1147         WARN("read failed: %d (%s)\n", errno, strerror(errno));
1148         return;
1149     }
1150
1151     This->held_frames += nread / This->fmt->nBlockAlign;
1152
1153     if(This->held_frames > This->bufsize_frames){
1154         WARN("Overflow of unread data\n");
1155         This->lcl_offs_frames += This->held_frames;
1156         This->lcl_offs_frames %= This->bufsize_frames;
1157         This->held_frames = This->bufsize_frames;
1158     }
1159 }
1160
1161 static void CALLBACK oss_period_callback(void *user, BOOLEAN timer)
1162 {
1163     ACImpl *This = user;
1164
1165     EnterCriticalSection(&This->lock);
1166
1167     if(This->dataflow == eRender)
1168         oss_write_data(This);
1169     else if(This->dataflow == eCapture)
1170         oss_read_data(This);
1171
1172     if(This->event)
1173         SetEvent(This->event);
1174
1175     LeaveCriticalSection(&This->lock);
1176 }
1177
1178 static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
1179 {
1180     ACImpl *This = impl_from_IAudioClient(iface);
1181     int mask;
1182
1183     TRACE("(%p)\n", This);
1184
1185     EnterCriticalSection(&This->lock);
1186
1187     if(!This->initted){
1188         LeaveCriticalSection(&This->lock);
1189         return AUDCLNT_E_NOT_INITIALIZED;
1190     }
1191
1192     if((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event){
1193         LeaveCriticalSection(&This->lock);
1194         return AUDCLNT_E_EVENTHANDLE_NOT_SET;
1195     }
1196
1197     if(This->playing){
1198         LeaveCriticalSection(&This->lock);
1199         return AUDCLNT_E_NOT_STOPPED;
1200     }
1201
1202     if(This->dataflow == eRender)
1203         mask = PCM_ENABLE_OUTPUT;
1204     else if(This->dataflow == eCapture)
1205         mask = PCM_ENABLE_INPUT;
1206     else{
1207         LeaveCriticalSection(&This->lock);
1208         return E_UNEXPECTED;
1209     }
1210
1211     if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1212         LeaveCriticalSection(&This->lock);
1213         WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1214         return E_FAIL;
1215     }
1216
1217     if(!CreateTimerQueueTimer(&This->timer, g_timer_q,
1218                 oss_period_callback, This, 0, This->period_us / 1000,
1219                 WT_EXECUTEINTIMERTHREAD))
1220         ERR("Unable to create period timer: %u\n", GetLastError());
1221
1222     This->playing = TRUE;
1223
1224     LeaveCriticalSection(&This->lock);
1225
1226     return S_OK;
1227 }
1228
1229 static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
1230 {
1231     ACImpl *This = impl_from_IAudioClient(iface);
1232     int mask;
1233
1234     TRACE("(%p)\n", This);
1235
1236     EnterCriticalSection(&This->lock);
1237
1238     if(!This->initted){
1239         LeaveCriticalSection(&This->lock);
1240         return AUDCLNT_E_NOT_INITIALIZED;
1241     }
1242
1243     if(!This->playing){
1244         LeaveCriticalSection(&This->lock);
1245         return S_FALSE;
1246     }
1247
1248     if(This->timer && This->timer != INVALID_HANDLE_VALUE){
1249         DeleteTimerQueueTimer(g_timer_q, This->timer,
1250                 INVALID_HANDLE_VALUE);
1251         This->timer = NULL;
1252     }
1253
1254     if(ioctl(This->fd, SNDCTL_DSP_HALT, NULL) < 0){
1255         LeaveCriticalSection(&This->lock);
1256         WARN("HALT failed: %d (%s)\n", errno, strerror(errno));
1257         return E_FAIL;
1258     }
1259
1260     mask = 0;
1261     if(ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &mask) < 0){
1262         LeaveCriticalSection(&This->lock);
1263         WARN("SETTRIGGER failed: %d (%s)\n", errno, strerror(errno));
1264         return E_FAIL;
1265     }
1266
1267     This->playing = FALSE;
1268
1269     LeaveCriticalSection(&This->lock);
1270
1271     return S_OK;
1272 }
1273
1274 static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
1275 {
1276     ACImpl *This = impl_from_IAudioClient(iface);
1277
1278     TRACE("(%p)\n", This);
1279
1280     EnterCriticalSection(&This->lock);
1281
1282     if(!This->initted){
1283         LeaveCriticalSection(&This->lock);
1284         return AUDCLNT_E_NOT_INITIALIZED;
1285     }
1286
1287     if(This->playing){
1288         LeaveCriticalSection(&This->lock);
1289         return AUDCLNT_E_NOT_STOPPED;
1290     }
1291
1292     This->written_frames = 0;
1293     This->inbuf_frames = 0;
1294     This->held_frames = 0;
1295
1296     if(ioctl(This->fd, SNDCTL_DSP_SKIP, NULL) < 0)
1297         WARN("SKIP failed: %d (%s)\n", errno, strerror(errno));
1298
1299     LeaveCriticalSection(&This->lock);
1300
1301     return S_OK;
1302 }
1303
1304 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
1305         HANDLE event)
1306 {
1307     ACImpl *This = impl_from_IAudioClient(iface);
1308
1309     TRACE("(%p)->(%p)\n", This, event);
1310
1311     if(!event)
1312         return E_INVALIDARG;
1313
1314     EnterCriticalSection(&This->lock);
1315
1316     if(!This->initted){
1317         LeaveCriticalSection(&This->lock);
1318         return AUDCLNT_E_NOT_INITIALIZED;
1319     }
1320
1321     if(!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)){
1322         LeaveCriticalSection(&This->lock);
1323         return AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
1324     }
1325
1326     This->event = event;
1327
1328     LeaveCriticalSection(&This->lock);
1329
1330     return S_OK;
1331 }
1332
1333 static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
1334         void **ppv)
1335 {
1336     ACImpl *This = impl_from_IAudioClient(iface);
1337
1338     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1339
1340     if(!ppv)
1341         return E_POINTER;
1342     *ppv = NULL;
1343
1344     EnterCriticalSection(&This->lock);
1345
1346     if(!This->initted){
1347         LeaveCriticalSection(&This->lock);
1348         return AUDCLNT_E_NOT_INITIALIZED;
1349     }
1350
1351     if(IsEqualIID(riid, &IID_IAudioRenderClient)){
1352         if(This->dataflow != eRender){
1353             LeaveCriticalSection(&This->lock);
1354             return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1355         }
1356         *ppv = &This->IAudioRenderClient_iface;
1357     }else if(IsEqualIID(riid, &IID_IAudioCaptureClient)){
1358         if(This->dataflow != eCapture){
1359             LeaveCriticalSection(&This->lock);
1360             return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1361         }
1362         *ppv = &This->IAudioCaptureClient_iface;
1363     }else if(IsEqualIID(riid, &IID_IAudioClock)){
1364         *ppv = &This->IAudioClock_iface;
1365     }else if(IsEqualIID(riid, &IID_IAudioStreamVolume)){
1366         *ppv = &This->IAudioStreamVolume_iface;
1367     }else if(IsEqualIID(riid, &IID_IAudioSessionControl)){
1368         if(!This->session_wrapper){
1369             This->session_wrapper = AudioSessionWrapper_Create(This);
1370             if(!This->session_wrapper){
1371                 LeaveCriticalSection(&This->lock);
1372                 return E_OUTOFMEMORY;
1373             }
1374         }
1375
1376         *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1377     }else if(IsEqualIID(riid, &IID_IChannelAudioVolume)){
1378         if(!This->session_wrapper){
1379             This->session_wrapper = AudioSessionWrapper_Create(This);
1380             if(!This->session_wrapper){
1381                 LeaveCriticalSection(&This->lock);
1382                 return E_OUTOFMEMORY;
1383             }
1384         }
1385
1386         *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1387     }else if(IsEqualIID(riid, &IID_ISimpleAudioVolume)){
1388         if(!This->session_wrapper){
1389             This->session_wrapper = AudioSessionWrapper_Create(This);
1390             if(!This->session_wrapper){
1391                 LeaveCriticalSection(&This->lock);
1392                 return E_OUTOFMEMORY;
1393             }
1394         }
1395
1396         *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1397     }
1398
1399     if(*ppv){
1400         IUnknown_AddRef((IUnknown*)*ppv);
1401         LeaveCriticalSection(&This->lock);
1402         return S_OK;
1403     }
1404
1405     LeaveCriticalSection(&This->lock);
1406
1407     FIXME("stub %s\n", debugstr_guid(riid));
1408     return E_NOINTERFACE;
1409 }
1410
1411 static const IAudioClientVtbl AudioClient_Vtbl =
1412 {
1413     AudioClient_QueryInterface,
1414     AudioClient_AddRef,
1415     AudioClient_Release,
1416     AudioClient_Initialize,
1417     AudioClient_GetBufferSize,
1418     AudioClient_GetStreamLatency,
1419     AudioClient_GetCurrentPadding,
1420     AudioClient_IsFormatSupported,
1421     AudioClient_GetMixFormat,
1422     AudioClient_GetDevicePeriod,
1423     AudioClient_Start,
1424     AudioClient_Stop,
1425     AudioClient_Reset,
1426     AudioClient_SetEventHandle,
1427     AudioClient_GetService
1428 };
1429
1430 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1431         IAudioRenderClient *iface, REFIID riid, void **ppv)
1432 {
1433     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1434
1435     if(!ppv)
1436         return E_POINTER;
1437     *ppv = NULL;
1438
1439     if(IsEqualIID(riid, &IID_IUnknown) ||
1440             IsEqualIID(riid, &IID_IAudioRenderClient))
1441         *ppv = iface;
1442     if(*ppv){
1443         IUnknown_AddRef((IUnknown*)*ppv);
1444         return S_OK;
1445     }
1446
1447     WARN("Unknown interface %s\n", debugstr_guid(riid));
1448     return E_NOINTERFACE;
1449 }
1450
1451 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1452 {
1453     ACImpl *This = impl_from_IAudioRenderClient(iface);
1454     return AudioClient_AddRef(&This->IAudioClient_iface);
1455 }
1456
1457 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1458 {
1459     ACImpl *This = impl_from_IAudioRenderClient(iface);
1460     return AudioClient_Release(&This->IAudioClient_iface);
1461 }
1462
1463 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1464         UINT32 frames, BYTE **data)
1465 {
1466     ACImpl *This = impl_from_IAudioRenderClient(iface);
1467     UINT32 pad, write_pos;
1468     HRESULT hr;
1469
1470     TRACE("(%p)->(%u, %p)\n", This, frames, data);
1471
1472     if(!data)
1473         return E_POINTER;
1474
1475     EnterCriticalSection(&This->lock);
1476
1477     if(This->buf_state != NOT_LOCKED){
1478         LeaveCriticalSection(&This->lock);
1479         return AUDCLNT_E_OUT_OF_ORDER;
1480     }
1481
1482     if(!frames){
1483         This->buf_state = LOCKED_NORMAL;
1484         LeaveCriticalSection(&This->lock);
1485         return S_OK;
1486     }
1487
1488     hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1489     if(FAILED(hr)){
1490         LeaveCriticalSection(&This->lock);
1491         return hr;
1492     }
1493
1494     if(pad + frames > This->bufsize_frames){
1495         LeaveCriticalSection(&This->lock);
1496         return AUDCLNT_E_BUFFER_TOO_LARGE;
1497     }
1498
1499     write_pos =
1500         (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1501     if(write_pos + frames > This->bufsize_frames){
1502         if(This->tmp_buffer_frames < frames){
1503             if(This->tmp_buffer)
1504                 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1505                         This->tmp_buffer, frames * This->fmt->nBlockAlign);
1506             else
1507                 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1508                         frames * This->fmt->nBlockAlign);
1509             if(!This->tmp_buffer){
1510                 LeaveCriticalSection(&This->lock);
1511                 return E_OUTOFMEMORY;
1512             }
1513             This->tmp_buffer_frames = frames;
1514         }
1515         *data = This->tmp_buffer;
1516         This->buf_state = LOCKED_WRAPPED;
1517     }else{
1518         *data = This->local_buffer +
1519             This->lcl_offs_frames * This->fmt->nBlockAlign;
1520         This->buf_state = LOCKED_NORMAL;
1521     }
1522
1523     LeaveCriticalSection(&This->lock);
1524
1525     return S_OK;
1526 }
1527
1528 static void oss_wrap_buffer(ACImpl *This, BYTE *buffer, UINT32 written_bytes)
1529 {
1530     UINT32 write_offs_frames =
1531         (This->lcl_offs_frames + This->held_frames) % This->bufsize_frames;
1532     UINT32 write_offs_bytes = write_offs_frames * This->fmt->nBlockAlign;
1533     UINT32 chunk_frames = This->bufsize_frames - write_offs_frames;
1534     UINT32 chunk_bytes = chunk_frames * This->fmt->nBlockAlign;
1535
1536     if(written_bytes < chunk_bytes){
1537         memcpy(This->local_buffer + write_offs_bytes, buffer, written_bytes);
1538     }else{
1539         memcpy(This->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1540         memcpy(This->local_buffer, buffer + chunk_bytes,
1541                 written_bytes - chunk_bytes);
1542     }
1543 }
1544
1545 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1546         IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1547 {
1548     ACImpl *This = impl_from_IAudioRenderClient(iface);
1549     BYTE *buffer;
1550     UINT32 written_bytes = written_frames * This->fmt->nBlockAlign;
1551
1552     TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1553
1554     EnterCriticalSection(&This->lock);
1555
1556     if(This->buf_state == NOT_LOCKED || !written_frames){
1557         This->buf_state = NOT_LOCKED;
1558         LeaveCriticalSection(&This->lock);
1559         return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
1560     }
1561
1562     if(This->buf_state == LOCKED_NORMAL)
1563         buffer = This->local_buffer +
1564             This->lcl_offs_frames * This->fmt->nBlockAlign;
1565     else
1566         buffer = This->tmp_buffer;
1567
1568     if(flags & AUDCLNT_BUFFERFLAGS_SILENT){
1569         WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)This->fmt;
1570         if((This->fmt->wFormatTag == WAVE_FORMAT_PCM ||
1571                 (This->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1572                  IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
1573                 This->fmt->wBitsPerSample == 8)
1574             memset(buffer, 128, written_frames * This->fmt->nBlockAlign);
1575         else
1576             memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
1577     }
1578
1579     if(This->held_frames){
1580         if(This->buf_state == LOCKED_WRAPPED)
1581             oss_wrap_buffer(This, buffer, written_bytes);
1582
1583         This->held_frames += written_frames;
1584     }else{
1585         ssize_t w_bytes;
1586         UINT32 w_frames;
1587
1588         w_bytes = write(This->fd, buffer,
1589                 written_frames * This->fmt->nBlockAlign);
1590         if(w_bytes < 0){
1591             LeaveCriticalSection(&This->lock);
1592             WARN("write failed: %d (%s)\n", errno, strerror(errno));
1593             return E_FAIL;
1594         }
1595         w_frames = w_bytes / This->fmt->nBlockAlign;
1596         This->inbuf_frames += w_frames;
1597
1598         if(w_frames < written_frames){
1599             if(This->buf_state == LOCKED_WRAPPED)
1600                 oss_wrap_buffer(This, This->tmp_buffer + w_bytes,
1601                         written_frames - w_frames);
1602
1603             This->held_frames = written_frames - w_frames;
1604         }
1605     }
1606
1607     This->written_frames += written_frames;
1608     This->buf_state = NOT_LOCKED;
1609
1610     LeaveCriticalSection(&This->lock);
1611
1612     return S_OK;
1613 }
1614
1615 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1616     AudioRenderClient_QueryInterface,
1617     AudioRenderClient_AddRef,
1618     AudioRenderClient_Release,
1619     AudioRenderClient_GetBuffer,
1620     AudioRenderClient_ReleaseBuffer
1621 };
1622
1623 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1624         IAudioCaptureClient *iface, REFIID riid, void **ppv)
1625 {
1626     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1627
1628     if(!ppv)
1629         return E_POINTER;
1630     *ppv = NULL;
1631
1632     if(IsEqualIID(riid, &IID_IUnknown) ||
1633             IsEqualIID(riid, &IID_IAudioCaptureClient))
1634         *ppv = iface;
1635     if(*ppv){
1636         IUnknown_AddRef((IUnknown*)*ppv);
1637         return S_OK;
1638     }
1639
1640     WARN("Unknown interface %s\n", debugstr_guid(riid));
1641     return E_NOINTERFACE;
1642 }
1643
1644 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1645 {
1646     ACImpl *This = impl_from_IAudioCaptureClient(iface);
1647     return IAudioClient_AddRef(&This->IAudioClient_iface);
1648 }
1649
1650 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1651 {
1652     ACImpl *This = impl_from_IAudioCaptureClient(iface);
1653     return IAudioClient_Release(&This->IAudioClient_iface);
1654 }
1655
1656 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1657         BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1658         UINT64 *qpcpos)
1659 {
1660     ACImpl *This = impl_from_IAudioCaptureClient(iface);
1661     HRESULT hr;
1662
1663     TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1664             devpos, qpcpos);
1665
1666     if(!data || !frames || !flags)
1667         return E_POINTER;
1668
1669     EnterCriticalSection(&This->lock);
1670
1671     if(This->buf_state != NOT_LOCKED){
1672         LeaveCriticalSection(&This->lock);
1673         return AUDCLNT_E_OUT_OF_ORDER;
1674     }
1675
1676     hr = IAudioCaptureClient_GetNextPacketSize(iface, frames);
1677     if(FAILED(hr)){
1678         LeaveCriticalSection(&This->lock);
1679         return hr;
1680     }
1681
1682     *flags = 0;
1683
1684     if(This->lcl_offs_frames + *frames > This->bufsize_frames){
1685         UINT32 chunk_bytes, offs_bytes, frames_bytes;
1686         if(This->tmp_buffer_frames < *frames){
1687             if(This->tmp_buffer)
1688                 This->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0,
1689                         This->tmp_buffer, *frames * This->fmt->nBlockAlign);
1690             else
1691                 This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0,
1692                         *frames * This->fmt->nBlockAlign);
1693             if(!This->tmp_buffer){
1694                 LeaveCriticalSection(&This->lock);
1695                 return E_OUTOFMEMORY;
1696             }
1697             This->tmp_buffer_frames = *frames;
1698         }
1699
1700         *data = This->tmp_buffer;
1701         chunk_bytes = (This->bufsize_frames - This->lcl_offs_frames) *
1702             This->fmt->nBlockAlign;
1703         offs_bytes = This->lcl_offs_frames * This->fmt->nBlockAlign;
1704         frames_bytes = *frames * This->fmt->nBlockAlign;
1705         memcpy(This->tmp_buffer, This->local_buffer + offs_bytes, chunk_bytes);
1706         memcpy(This->tmp_buffer, This->local_buffer,
1707                 frames_bytes - chunk_bytes);
1708     }else
1709         *data = This->local_buffer +
1710             This->lcl_offs_frames * This->fmt->nBlockAlign;
1711
1712     This->buf_state = LOCKED_NORMAL;
1713
1714     if(devpos || qpcpos)
1715         IAudioClock_GetPosition(&This->IAudioClock_iface, devpos, qpcpos);
1716
1717     LeaveCriticalSection(&This->lock);
1718
1719     return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
1720 }
1721
1722 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1723         IAudioCaptureClient *iface, UINT32 done)
1724 {
1725     ACImpl *This = impl_from_IAudioCaptureClient(iface);
1726
1727     TRACE("(%p)->(%u)\n", This, done);
1728
1729     EnterCriticalSection(&This->lock);
1730
1731     if(This->buf_state == NOT_LOCKED){
1732         LeaveCriticalSection(&This->lock);
1733         return AUDCLNT_E_OUT_OF_ORDER;
1734     }
1735
1736     This->held_frames -= done;
1737     This->lcl_offs_frames += done;
1738     This->lcl_offs_frames %= This->bufsize_frames;
1739
1740     This->buf_state = NOT_LOCKED;
1741
1742     LeaveCriticalSection(&This->lock);
1743
1744     return S_OK;
1745 }
1746
1747 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1748         IAudioCaptureClient *iface, UINT32 *frames)
1749 {
1750     ACImpl *This = impl_from_IAudioCaptureClient(iface);
1751
1752     TRACE("(%p)->(%p)\n", This, frames);
1753
1754     return AudioClient_GetCurrentPadding(&This->IAudioClient_iface, frames);
1755 }
1756
1757 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1758 {
1759     AudioCaptureClient_QueryInterface,
1760     AudioCaptureClient_AddRef,
1761     AudioCaptureClient_Release,
1762     AudioCaptureClient_GetBuffer,
1763     AudioCaptureClient_ReleaseBuffer,
1764     AudioCaptureClient_GetNextPacketSize
1765 };
1766
1767 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1768         REFIID riid, void **ppv)
1769 {
1770     ACImpl *This = impl_from_IAudioClock(iface);
1771
1772     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1773
1774     if(!ppv)
1775         return E_POINTER;
1776     *ppv = NULL;
1777
1778     if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
1779         *ppv = iface;
1780     else if(IsEqualIID(riid, &IID_IAudioClock2))
1781         *ppv = &This->IAudioClock2_iface;
1782     if(*ppv){
1783         IUnknown_AddRef((IUnknown*)*ppv);
1784         return S_OK;
1785     }
1786
1787     WARN("Unknown interface %s\n", debugstr_guid(riid));
1788     return E_NOINTERFACE;
1789 }
1790
1791 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
1792 {
1793     ACImpl *This = impl_from_IAudioClock(iface);
1794     return IAudioClient_AddRef(&This->IAudioClient_iface);
1795 }
1796
1797 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
1798 {
1799     ACImpl *This = impl_from_IAudioClock(iface);
1800     return IAudioClient_Release(&This->IAudioClient_iface);
1801 }
1802
1803 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
1804 {
1805     ACImpl *This = impl_from_IAudioClock(iface);
1806
1807     TRACE("(%p)->(%p)\n", This, freq);
1808
1809     *freq = This->fmt->nSamplesPerSec;
1810
1811     return S_OK;
1812 }
1813
1814 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
1815         UINT64 *qpctime)
1816 {
1817     ACImpl *This = impl_from_IAudioClock(iface);
1818     UINT32 pad;
1819     HRESULT hr;
1820
1821     TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
1822
1823     if(!pos)
1824         return E_POINTER;
1825
1826     EnterCriticalSection(&This->lock);
1827
1828     hr = IAudioClient_GetCurrentPadding(&This->IAudioClient_iface, &pad);
1829     if(FAILED(hr)){
1830         LeaveCriticalSection(&This->lock);
1831         return hr;
1832     }
1833
1834     if(This->dataflow == eRender)
1835         *pos = This->written_frames - pad;
1836     else if(This->dataflow == eCapture)
1837         *pos = This->written_frames + pad;
1838
1839     LeaveCriticalSection(&This->lock);
1840
1841     if(qpctime){
1842         LARGE_INTEGER stamp, freq;
1843         QueryPerformanceCounter(&stamp);
1844         QueryPerformanceFrequency(&freq);
1845         *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1846     }
1847
1848     return S_OK;
1849 }
1850
1851 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
1852         DWORD *chars)
1853 {
1854     ACImpl *This = impl_from_IAudioClock(iface);
1855
1856     TRACE("(%p)->(%p)\n", This, chars);
1857
1858     if(!chars)
1859         return E_POINTER;
1860
1861     *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
1862
1863     return S_OK;
1864 }
1865
1866 static const IAudioClockVtbl AudioClock_Vtbl =
1867 {
1868     AudioClock_QueryInterface,
1869     AudioClock_AddRef,
1870     AudioClock_Release,
1871     AudioClock_GetFrequency,
1872     AudioClock_GetPosition,
1873     AudioClock_GetCharacteristics
1874 };
1875
1876 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
1877         REFIID riid, void **ppv)
1878 {
1879     ACImpl *This = impl_from_IAudioClock2(iface);
1880     return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
1881 }
1882
1883 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
1884 {
1885     ACImpl *This = impl_from_IAudioClock2(iface);
1886     return IAudioClient_AddRef(&This->IAudioClient_iface);
1887 }
1888
1889 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
1890 {
1891     ACImpl *This = impl_from_IAudioClock2(iface);
1892     return IAudioClient_Release(&This->IAudioClient_iface);
1893 }
1894
1895 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
1896         UINT64 *pos, UINT64 *qpctime)
1897 {
1898     ACImpl *This = impl_from_IAudioClock2(iface);
1899
1900     FIXME("(%p)->(%p, %p)\n", This, pos, qpctime);
1901
1902     return E_NOTIMPL;
1903 }
1904
1905 static const IAudioClock2Vtbl AudioClock2_Vtbl =
1906 {
1907     AudioClock2_QueryInterface,
1908     AudioClock2_AddRef,
1909     AudioClock2_Release,
1910     AudioClock2_GetDevicePosition
1911 };
1912
1913 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
1914 {
1915     AudioSessionWrapper *ret;
1916
1917     ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1918             sizeof(AudioSessionWrapper));
1919     if(!ret)
1920         return NULL;
1921
1922     ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
1923     ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
1924     ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
1925
1926     ret->client = client;
1927     ret->session = client->session;
1928     AudioClient_AddRef(&client->IAudioClient_iface);
1929
1930     return ret;
1931 }
1932
1933 static HRESULT WINAPI AudioSessionControl_QueryInterface(
1934         IAudioSessionControl2 *iface, REFIID riid, void **ppv)
1935 {
1936     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1937
1938     if(!ppv)
1939         return E_POINTER;
1940     *ppv = NULL;
1941
1942     if(IsEqualIID(riid, &IID_IUnknown) ||
1943             IsEqualIID(riid, &IID_IAudioSessionControl) ||
1944             IsEqualIID(riid, &IID_IAudioSessionControl2))
1945         *ppv = iface;
1946     if(*ppv){
1947         IUnknown_AddRef((IUnknown*)*ppv);
1948         return S_OK;
1949     }
1950
1951     WARN("Unknown interface %s\n", debugstr_guid(riid));
1952     return E_NOINTERFACE;
1953 }
1954
1955 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
1956 {
1957     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1958     ULONG ref;
1959     ref = InterlockedIncrement(&This->ref);
1960     TRACE("(%p) Refcount now %u\n", This, ref);
1961     return ref;
1962 }
1963
1964 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
1965 {
1966     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1967     ULONG ref;
1968     ref = InterlockedDecrement(&This->ref);
1969     TRACE("(%p) Refcount now %u\n", This, ref);
1970     if(!ref){
1971         EnterCriticalSection(&This->client->lock);
1972         This->client->session_wrapper = NULL;
1973         LeaveCriticalSection(&This->client->lock);
1974         AudioClient_Release(&This->client->IAudioClient_iface);
1975         HeapFree(GetProcessHeap(), 0, This);
1976     }
1977     return ref;
1978 }
1979
1980 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
1981         AudioSessionState *state)
1982 {
1983     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1984     ACImpl *client;
1985
1986     TRACE("(%p)->(%p)\n", This, state);
1987
1988     if(!state)
1989         return NULL_PTR_ERR;
1990
1991     EnterCriticalSection(&g_sessions_lock);
1992
1993     if(list_empty(&This->session->clients)){
1994         *state = AudioSessionStateExpired;
1995         LeaveCriticalSection(&g_sessions_lock);
1996         return S_OK;
1997     }
1998
1999     LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry){
2000         EnterCriticalSection(&client->lock);
2001         if(client->playing){
2002             *state = AudioSessionStateActive;
2003             LeaveCriticalSection(&client->lock);
2004             LeaveCriticalSection(&g_sessions_lock);
2005             return S_OK;
2006         }
2007         LeaveCriticalSection(&client->lock);
2008     }
2009
2010     LeaveCriticalSection(&g_sessions_lock);
2011
2012     *state = AudioSessionStateInactive;
2013
2014     return S_OK;
2015 }
2016
2017 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2018         IAudioSessionControl2 *iface, WCHAR **name)
2019 {
2020     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2021
2022     FIXME("(%p)->(%p) - stub\n", This, name);
2023
2024     return E_NOTIMPL;
2025 }
2026
2027 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2028         IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2029 {
2030     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2031
2032     FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2033
2034     return E_NOTIMPL;
2035 }
2036
2037 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2038         IAudioSessionControl2 *iface, WCHAR **path)
2039 {
2040     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2041
2042     FIXME("(%p)->(%p) - stub\n", This, path);
2043
2044     return E_NOTIMPL;
2045 }
2046
2047 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2048         IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2049 {
2050     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2051
2052     FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2053
2054     return E_NOTIMPL;
2055 }
2056
2057 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2058         IAudioSessionControl2 *iface, GUID *group)
2059 {
2060     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2061
2062     FIXME("(%p)->(%p) - stub\n", This, group);
2063
2064     return E_NOTIMPL;
2065 }
2066
2067 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2068         IAudioSessionControl2 *iface, GUID *group, const GUID *session)
2069 {
2070     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2071
2072     FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2073             debugstr_guid(session));
2074
2075     return E_NOTIMPL;
2076 }
2077
2078 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2079         IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2080 {
2081     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2082
2083     FIXME("(%p)->(%p) - stub\n", This, events);
2084
2085     return S_OK;
2086 }
2087
2088 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2089         IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2090 {
2091     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2092
2093     FIXME("(%p)->(%p) - stub\n", This, events);
2094
2095     return S_OK;
2096 }
2097
2098 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2099         IAudioSessionControl2 *iface, WCHAR **id)
2100 {
2101     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2102
2103     FIXME("(%p)->(%p) - stub\n", This, id);
2104
2105     return E_NOTIMPL;
2106 }
2107
2108 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2109         IAudioSessionControl2 *iface, WCHAR **id)
2110 {
2111     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2112
2113     FIXME("(%p)->(%p) - stub\n", This, id);
2114
2115     return E_NOTIMPL;
2116 }
2117
2118 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2119         IAudioSessionControl2 *iface, DWORD *pid)
2120 {
2121     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2122
2123     TRACE("(%p)->(%p)\n", This, pid);
2124
2125     if(!pid)
2126         return E_POINTER;
2127
2128     *pid = GetCurrentProcessId();
2129
2130     return S_OK;
2131 }
2132
2133 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2134         IAudioSessionControl2 *iface)
2135 {
2136     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2137
2138     TRACE("(%p)\n", This);
2139
2140     return S_FALSE;
2141 }
2142
2143 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2144         IAudioSessionControl2 *iface, BOOL optout)
2145 {
2146     AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2147
2148     TRACE("(%p)->(%d)\n", This, optout);
2149
2150     return S_OK;
2151 }
2152
2153 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2154 {
2155     AudioSessionControl_QueryInterface,
2156     AudioSessionControl_AddRef,
2157     AudioSessionControl_Release,
2158     AudioSessionControl_GetState,
2159     AudioSessionControl_GetDisplayName,
2160     AudioSessionControl_SetDisplayName,
2161     AudioSessionControl_GetIconPath,
2162     AudioSessionControl_SetIconPath,
2163     AudioSessionControl_GetGroupingParam,
2164     AudioSessionControl_SetGroupingParam,
2165     AudioSessionControl_RegisterAudioSessionNotification,
2166     AudioSessionControl_UnregisterAudioSessionNotification,
2167     AudioSessionControl_GetSessionIdentifier,
2168     AudioSessionControl_GetSessionInstanceIdentifier,
2169     AudioSessionControl_GetProcessId,
2170     AudioSessionControl_IsSystemSoundsSession,
2171     AudioSessionControl_SetDuckingPreference
2172 };
2173
2174 /* index == -1 means set all channels, otherwise sets only the given channel */
2175 static HRESULT oss_setvol(ACImpl *This, UINT32 index)
2176 {
2177     int setreq, getreq;
2178     unsigned int vol;
2179     unsigned short l;
2180     float level;
2181
2182     if(index == (UINT32)-1){
2183         HRESULT ret = S_OK;
2184         UINT32 i;
2185         for(i = 0; i < This->fmt->nChannels; ++i){
2186             HRESULT hr;
2187             hr = oss_setvol(This, i);
2188             if(FAILED(hr))
2189                 ret = hr;
2190         }
2191         return ret;
2192     }
2193
2194     if(index > 1)
2195         /* OSS doesn't support volume control past the first two channels */
2196         return S_OK;
2197
2198     if(This->dataflow == eRender){
2199         setreq = SNDCTL_DSP_SETPLAYVOL;
2200         getreq = SNDCTL_DSP_GETPLAYVOL;
2201     }else if(This->dataflow == eCapture){
2202         setreq = SNDCTL_DSP_SETRECVOL;
2203         getreq = SNDCTL_DSP_GETRECVOL;
2204     }else
2205         return E_UNEXPECTED;
2206
2207     if(ioctl(This->fd, getreq, &vol) < 0){
2208         if(errno == EINVAL)
2209             /* device doesn't support this call */
2210             return S_OK;
2211
2212         WARN("GET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2213         return E_FAIL;
2214     }
2215
2216     level = This->session->master_vol * This->session->channel_vols[index] *
2217         This->vols[index];
2218     l = level * 100;
2219     if(index == 0)
2220         vol = l | (vol & 0xFF00);
2221     else
2222         vol = (vol & 0xFF) | (l << 8);
2223
2224     if(ioctl(This->fd, setreq, &vol) < 0){
2225         if(errno == EINVAL)
2226             /* device doesn't support this call */
2227             return S_OK;
2228
2229         WARN("SET[REC|PLAY]VOL failed: %d (%s)\n", errno, strerror(errno));
2230         return E_FAIL;
2231     }
2232
2233     return S_OK;
2234 }
2235
2236 static HRESULT oss_session_setvol(AudioSession *session, UINT32 index)
2237 {
2238     HRESULT ret = S_OK;
2239     ACImpl *client;
2240
2241     LIST_FOR_EACH_ENTRY(client, &session->clients, ACImpl, entry){
2242         HRESULT hr;
2243         hr = oss_setvol(client, index);
2244         if(FAILED(hr))
2245             ret = hr;
2246     }
2247
2248     return ret;
2249 }
2250
2251 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2252         ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2253 {
2254     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2255
2256     if(!ppv)
2257         return E_POINTER;
2258     *ppv = NULL;
2259
2260     if(IsEqualIID(riid, &IID_IUnknown) ||
2261             IsEqualIID(riid, &IID_ISimpleAudioVolume))
2262         *ppv = iface;
2263     if(*ppv){
2264         IUnknown_AddRef((IUnknown*)*ppv);
2265         return S_OK;
2266     }
2267
2268     WARN("Unknown interface %s\n", debugstr_guid(riid));
2269     return E_NOINTERFACE;
2270 }
2271
2272 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2273 {
2274     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2275     return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2276 }
2277
2278 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2279 {
2280     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2281     return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2282 }
2283
2284 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2285         ISimpleAudioVolume *iface, float level, const GUID *context)
2286 {
2287     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2288     AudioSession *session = This->session;
2289     HRESULT ret;
2290
2291     TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2292
2293     if(level < 0.f || level > 1.f)
2294         return E_INVALIDARG;
2295
2296     if(context)
2297         FIXME("Notifications not supported yet\n");
2298
2299     EnterCriticalSection(&session->lock);
2300
2301     session->master_vol = level;
2302
2303     ret = oss_session_setvol(session, -1);
2304
2305     LeaveCriticalSection(&session->lock);
2306
2307     return ret;
2308 }
2309
2310 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2311         ISimpleAudioVolume *iface, float *level)
2312 {
2313     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2314     AudioSession *session = This->session;
2315
2316     TRACE("(%p)->(%p)\n", session, level);
2317
2318     if(!level)
2319         return NULL_PTR_ERR;
2320
2321     *level = session->master_vol;
2322
2323     return S_OK;
2324 }
2325
2326 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2327         BOOL mute, const GUID *context)
2328 {
2329     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2330     AudioSession *session = This->session;
2331
2332     FIXME("(%p)->(%u, %p) - stub\n", session, mute, context);
2333
2334     return E_NOTIMPL;
2335 }
2336
2337 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2338         BOOL *mute)
2339 {
2340     AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2341     AudioSession *session = This->session;
2342
2343     FIXME("(%p)->(%p) - stub\n", session, mute);
2344
2345     if(!mute)
2346         return NULL_PTR_ERR;
2347
2348     return E_NOTIMPL;
2349 }
2350
2351 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl  =
2352 {
2353     SimpleAudioVolume_QueryInterface,
2354     SimpleAudioVolume_AddRef,
2355     SimpleAudioVolume_Release,
2356     SimpleAudioVolume_SetMasterVolume,
2357     SimpleAudioVolume_GetMasterVolume,
2358     SimpleAudioVolume_SetMute,
2359     SimpleAudioVolume_GetMute
2360 };
2361
2362 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
2363         IAudioStreamVolume *iface, REFIID riid, void **ppv)
2364 {
2365     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2366
2367     if(!ppv)
2368         return E_POINTER;
2369     *ppv = NULL;
2370
2371     if(IsEqualIID(riid, &IID_IUnknown) ||
2372             IsEqualIID(riid, &IID_IAudioStreamVolume))
2373         *ppv = iface;
2374     if(*ppv){
2375         IUnknown_AddRef((IUnknown*)*ppv);
2376         return S_OK;
2377     }
2378
2379     WARN("Unknown interface %s\n", debugstr_guid(riid));
2380     return E_NOINTERFACE;
2381 }
2382
2383 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
2384 {
2385     ACImpl *This = impl_from_IAudioStreamVolume(iface);
2386     return IAudioClient_AddRef(&This->IAudioClient_iface);
2387 }
2388
2389 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
2390 {
2391     ACImpl *This = impl_from_IAudioStreamVolume(iface);
2392     return IAudioClient_Release(&This->IAudioClient_iface);
2393 }
2394
2395 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
2396         IAudioStreamVolume *iface, UINT32 *out)
2397 {
2398     ACImpl *This = impl_from_IAudioStreamVolume(iface);
2399
2400     TRACE("(%p)->(%p)\n", This, out);
2401
2402     if(!out)
2403         return E_POINTER;
2404
2405     *out = This->fmt->nChannels;
2406
2407     return S_OK;
2408 }
2409
2410 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
2411         IAudioStreamVolume *iface, UINT32 index, float level)
2412 {
2413     ACImpl *This = impl_from_IAudioStreamVolume(iface);
2414     HRESULT ret;
2415
2416     TRACE("(%p)->(%d, %f)\n", This, index, level);
2417
2418     if(level < 0.f || level > 1.f)
2419         return E_INVALIDARG;
2420
2421     if(index >= This->fmt->nChannels)
2422         return E_INVALIDARG;
2423
2424     EnterCriticalSection(&This->lock);
2425
2426     This->vols[index] = level;
2427
2428     ret = oss_setvol(This, index);
2429
2430     LeaveCriticalSection(&This->lock);
2431
2432     return ret;
2433 }
2434
2435 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
2436         IAudioStreamVolume *iface, UINT32 index, float *level)
2437 {
2438     ACImpl *This = impl_from_IAudioStreamVolume(iface);
2439
2440     TRACE("(%p)->(%d, %p)\n", This, index, level);
2441
2442     if(!level)
2443         return E_POINTER;
2444
2445     if(index >= This->fmt->nChannels)
2446         return E_INVALIDARG;
2447
2448     *level = This->vols[index];
2449
2450     return S_OK;
2451 }
2452
2453 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
2454         IAudioStreamVolume *iface, UINT32 count, const float *levels)
2455 {
2456     ACImpl *This = impl_from_IAudioStreamVolume(iface);
2457     int i;
2458     HRESULT ret;
2459
2460     TRACE("(%p)->(%d, %p)\n", This, count, levels);
2461
2462     if(!levels)
2463         return E_POINTER;
2464
2465     if(count != This->fmt->nChannels)
2466         return E_INVALIDARG;
2467
2468     EnterCriticalSection(&This->lock);
2469
2470     for(i = 0; i < count; ++i)
2471         This->vols[i] = levels[i];
2472
2473     ret = oss_setvol(This, -1);
2474
2475     LeaveCriticalSection(&This->lock);
2476
2477     return ret;
2478 }
2479
2480 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
2481         IAudioStreamVolume *iface, UINT32 count, float *levels)
2482 {
2483     ACImpl *This = impl_from_IAudioStreamVolume(iface);
2484     int i;
2485
2486     TRACE("(%p)->(%d, %p)\n", This, count, levels);
2487
2488     if(!levels)
2489         return E_POINTER;
2490
2491     if(count != This->fmt->nChannels)
2492         return E_INVALIDARG;
2493
2494     EnterCriticalSection(&This->lock);
2495
2496     for(i = 0; i < count; ++i)
2497         levels[i] = This->vols[i];
2498
2499     LeaveCriticalSection(&This->lock);
2500
2501     return S_OK;
2502 }
2503
2504 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
2505 {
2506     AudioStreamVolume_QueryInterface,
2507     AudioStreamVolume_AddRef,
2508     AudioStreamVolume_Release,
2509     AudioStreamVolume_GetChannelCount,
2510     AudioStreamVolume_SetChannelVolume,
2511     AudioStreamVolume_GetChannelVolume,
2512     AudioStreamVolume_SetAllVolumes,
2513     AudioStreamVolume_GetAllVolumes
2514 };
2515
2516 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2517         IChannelAudioVolume *iface, REFIID riid, void **ppv)
2518 {
2519     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2520
2521     if(!ppv)
2522         return E_POINTER;
2523     *ppv = NULL;
2524
2525     if(IsEqualIID(riid, &IID_IUnknown) ||
2526             IsEqualIID(riid, &IID_IChannelAudioVolume))
2527         *ppv = iface;
2528     if(*ppv){
2529         IUnknown_AddRef((IUnknown*)*ppv);
2530         return S_OK;
2531     }
2532
2533     WARN("Unknown interface %s\n", debugstr_guid(riid));
2534     return E_NOINTERFACE;
2535 }
2536
2537 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2538 {
2539     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2540     return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2541 }
2542
2543 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2544 {
2545     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2546     return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2547 }
2548
2549 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2550         IChannelAudioVolume *iface, UINT32 *out)
2551 {
2552     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2553     AudioSession *session = This->session;
2554
2555     TRACE("(%p)->(%p)\n", session, out);
2556
2557     if(!out)
2558         return NULL_PTR_ERR;
2559
2560     *out = session->channel_count;
2561
2562     return S_OK;
2563 }
2564
2565 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2566         IChannelAudioVolume *iface, UINT32 index, float level,
2567         const GUID *context)
2568 {
2569     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2570     AudioSession *session = This->session;
2571     HRESULT ret;
2572
2573     TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2574             wine_dbgstr_guid(context));
2575
2576     if(level < 0.f || level > 1.f)
2577         return E_INVALIDARG;
2578
2579     if(index >= session->channel_count)
2580         return E_INVALIDARG;
2581
2582     if(context)
2583         FIXME("Notifications not supported yet\n");
2584
2585     EnterCriticalSection(&session->lock);
2586
2587     session->channel_vols[index] = level;
2588
2589     ret = oss_session_setvol(session, index);
2590
2591     LeaveCriticalSection(&session->lock);
2592
2593     return ret;
2594 }
2595
2596 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2597         IChannelAudioVolume *iface, UINT32 index, float *level)
2598 {
2599     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2600     AudioSession *session = This->session;
2601
2602     TRACE("(%p)->(%d, %p)\n", session, index, level);
2603
2604     if(!level)
2605         return NULL_PTR_ERR;
2606
2607     if(index >= session->channel_count)
2608         return E_INVALIDARG;
2609
2610     *level = session->channel_vols[index];
2611
2612     return S_OK;
2613 }
2614
2615 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2616         IChannelAudioVolume *iface, UINT32 count, const float *levels,
2617         const GUID *context)
2618 {
2619     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2620     AudioSession *session = This->session;
2621     int i;
2622     HRESULT ret;
2623
2624     TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2625             wine_dbgstr_guid(context));
2626
2627     if(!levels)
2628         return NULL_PTR_ERR;
2629
2630     if(count != session->channel_count)
2631         return E_INVALIDARG;
2632
2633     if(context)
2634         FIXME("Notifications not supported yet\n");
2635
2636     EnterCriticalSection(&session->lock);
2637
2638     for(i = 0; i < count; ++i)
2639         session->channel_vols[i] = levels[i];
2640
2641     ret = oss_session_setvol(session, -1);
2642
2643     LeaveCriticalSection(&session->lock);
2644
2645     return ret;
2646 }
2647
2648 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2649         IChannelAudioVolume *iface, UINT32 count, float *levels)
2650 {
2651     AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2652     AudioSession *session = This->session;
2653     int i;
2654
2655     TRACE("(%p)->(%d, %p)\n", session, count, levels);
2656
2657     if(!levels)
2658         return NULL_PTR_ERR;
2659
2660     if(count != session->channel_count)
2661         return E_INVALIDARG;
2662
2663     for(i = 0; i < count; ++i)
2664         levels[i] = session->channel_vols[i];
2665
2666     return S_OK;
2667 }
2668
2669 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2670 {
2671     ChannelAudioVolume_QueryInterface,
2672     ChannelAudioVolume_AddRef,
2673     ChannelAudioVolume_Release,
2674     ChannelAudioVolume_GetChannelCount,
2675     ChannelAudioVolume_SetChannelVolume,
2676     ChannelAudioVolume_GetChannelVolume,
2677     ChannelAudioVolume_SetAllVolumes,
2678     ChannelAudioVolume_GetAllVolumes
2679 };