~ubuntu-branches/ubuntu/quantal/openal-soft/quantal

« back to all changes in this revision

Viewing changes to Alc/backends/mmdevapi.c

  • Committer: Package Import Robot
  • Author(s): Michael Terry
  • Date: 2012-05-22 10:14:53 UTC
  • mfrom: (7.1.9 sid)
  • Revision ID: package-import@ubuntu.com-20120522101453-knsv1m1m8vl5ccfp
Tags: 1:1.14-3ubuntu1
* Merge from Debian testing.  Remaining changes:
  - Add a symbols file for libopenal1
* debian/libopenal1.symbols:
  - Update for 1.14

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * OpenAL cross platform audio library
 
3
 * Copyright (C) 2011 by authors.
 
4
 * This library is free software; you can redistribute it and/or
 
5
 *  modify it under the terms of the GNU Library General Public
 
6
 *  License as published by the Free Software Foundation; either
 
7
 *  version 2 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
 *  Library General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Library General Public
 
15
 *  License along with this library; if not, write to the
 
16
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
17
 *  Boston, MA  02111-1307, USA.
 
18
 * Or go to http://www.gnu.org/copyleft/lgpl.html
 
19
 */
 
20
 
 
21
#include "config.h"
 
22
 
 
23
#define COBJMACROS
 
24
#include <stdlib.h>
 
25
#include <stdio.h>
 
26
#include <memory.h>
 
27
 
 
28
#include <mmdeviceapi.h>
 
29
#include <audioclient.h>
 
30
#include <cguid.h>
 
31
#include <mmreg.h>
 
32
#include <propsys.h>
 
33
#include <propkey.h>
 
34
#include <devpkey.h>
 
35
#ifndef _WAVEFORMATEXTENSIBLE_
 
36
#include <ks.h>
 
37
#include <ksmedia.h>
 
38
#endif
 
39
 
 
40
#include "alMain.h"
 
41
#include "AL/al.h"
 
42
#include "AL/alc.h"
 
43
 
 
44
 
 
45
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
 
46
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
 
47
 
 
48
#define MONO SPEAKER_FRONT_CENTER
 
49
#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
 
50
#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
 
51
#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
 
52
#define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
 
53
#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
 
54
#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
 
55
 
 
56
 
 
57
typedef struct {
 
58
    WCHAR *devid;
 
59
 
 
60
    IMMDevice *mmdev;
 
61
    IAudioClient *client;
 
62
    IAudioRenderClient *render;
 
63
    HANDLE hNotifyEvent;
 
64
 
 
65
    HANDLE MsgEvent;
 
66
 
 
67
    volatile int killNow;
 
68
    ALvoid *thread;
 
69
} MMDevApiData;
 
70
 
 
71
 
 
72
typedef struct {
 
73
    ALCchar *name;
 
74
    WCHAR *devid;
 
75
} DevMap;
 
76
 
 
77
static DevMap *PlaybackDeviceList;
 
78
static ALuint NumPlaybackDevices;
 
79
static DevMap *CaptureDeviceList;
 
80
static ALuint NumCaptureDevices;
 
81
 
 
82
 
 
83
static HANDLE ThreadHdl;
 
84
static DWORD ThreadID;
 
85
 
 
86
typedef struct {
 
87
    HANDLE FinishedEvt;
 
88
    HRESULT result;
 
89
} ThreadRequest;
 
90
 
 
91
#define WM_USER_OpenDevice  (WM_USER+0)
 
92
#define WM_USER_ResetDevice (WM_USER+1)
 
93
#define WM_USER_StartDevice (WM_USER+2)
 
94
#define WM_USER_StopDevice  (WM_USER+3)
 
95
#define WM_USER_CloseDevice (WM_USER+4)
 
96
#define WM_USER_Enumerate   (WM_USER+5)
 
97
 
 
98
static HRESULT WaitForResponse(ThreadRequest *req)
 
99
{
 
100
    if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
 
101
        return req->result;
 
102
    ERR("Message response error: %lu\n", GetLastError());
 
103
    return E_FAIL;
 
104
}
 
105
 
 
106
 
 
107
static ALCchar *get_device_name(IMMDevice *device)
 
108
{
 
109
    ALCchar *name = NULL;
 
110
    IPropertyStore *ps;
 
111
    PROPVARIANT pvname;
 
112
    HRESULT hr;
 
113
    int len;
 
114
 
 
115
    hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
 
116
    if(FAILED(hr))
 
117
    {
 
118
        WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
 
119
        return calloc(1, 1);
 
120
    }
 
121
 
 
122
    PropVariantInit(&pvname);
 
123
 
 
124
    hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
 
125
    if(FAILED(hr))
 
126
    {
 
127
        WARN("GetValue failed: 0x%08lx\n", hr);
 
128
        name = calloc(1, 1);
 
129
    }
 
130
    else
 
131
    {
 
132
        if((len=WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, NULL, 0, NULL, NULL)) > 0)
 
133
        {
 
134
            name = calloc(1, len);
 
135
            WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, name, len, NULL, NULL);
 
136
        }
 
137
    }
 
138
 
 
139
    PropVariantClear(&pvname);
 
140
    IPropertyStore_Release(ps);
 
141
 
 
142
    return name;
 
143
}
 
144
 
 
145
static void add_device(IMMDevice *device, DevMap *devmap)
 
146
{
 
147
    LPWSTR devid;
 
148
    HRESULT hr;
 
149
 
 
150
    hr = IMMDevice_GetId(device, &devid);
 
151
    if(SUCCEEDED(hr))
 
152
    {
 
153
        devmap->devid = strdupW(devid);
 
154
        devmap->name = get_device_name(device);
 
155
        TRACE("Got device \"%s\", \"%ls\"\n", devmap->name, devmap->devid);
 
156
        CoTaskMemFree(devid);
 
157
    }
 
158
}
 
159
 
 
160
static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALuint *numdevs)
 
161
{
 
162
    IMMDeviceCollection *coll;
 
163
    IMMDevice *defdev = NULL;
 
164
    DevMap *devlist = NULL;
 
165
    HRESULT hr;
 
166
    UINT count;
 
167
    UINT idx;
 
168
    UINT i;
 
169
 
 
170
    hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
 
171
    if(FAILED(hr))
 
172
    {
 
173
        ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
 
174
        return NULL;
 
175
    }
 
176
 
 
177
    idx = count = 0;
 
178
    hr = IMMDeviceCollection_GetCount(coll, &count);
 
179
    if(SUCCEEDED(hr) && count > 0)
 
180
    {
 
181
        devlist = calloc(count, sizeof(*devlist));
 
182
        if(!devlist)
 
183
        {
 
184
            IMMDeviceCollection_Release(coll);
 
185
            return NULL;
 
186
        }
 
187
 
 
188
        hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
 
189
                                                         eMultimedia, &defdev);
 
190
    }
 
191
    if(SUCCEEDED(hr) && defdev != NULL)
 
192
        add_device(defdev, &devlist[idx++]);
 
193
 
 
194
    for(i = 0;i < count && idx < count;++i)
 
195
    {
 
196
        IMMDevice *device;
 
197
 
 
198
        if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
 
199
            continue;
 
200
 
 
201
        if(device != defdev)
 
202
            add_device(device, &devlist[idx++]);
 
203
 
 
204
        IMMDevice_Release(device);
 
205
    }
 
206
 
 
207
    if(defdev) IMMDevice_Release(defdev);
 
208
    IMMDeviceCollection_Release(coll);
 
209
 
 
210
    *numdevs = idx;
 
211
    return devlist;
 
212
}
 
213
 
 
214
 
 
215
static ALuint MMDevApiProc(ALvoid *ptr)
 
216
{
 
217
    ALCdevice *device = ptr;
 
218
    MMDevApiData *data = device->ExtraData;
 
219
    UINT32 buffer_len, written;
 
220
    ALuint update_size, len;
 
221
    BYTE *buffer;
 
222
    HRESULT hr;
 
223
 
 
224
    hr = CoInitialize(NULL);
 
225
    if(FAILED(hr))
 
226
    {
 
227
        ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
 
228
        aluHandleDisconnect(device);
 
229
        return 0;
 
230
    }
 
231
 
 
232
    hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
 
233
    if(FAILED(hr))
 
234
    {
 
235
        ERR("Failed to get audio buffer size: 0x%08lx\n", hr);
 
236
        aluHandleDisconnect(device);
 
237
        CoUninitialize();
 
238
        return 0;
 
239
    }
 
240
 
 
241
    SetRTPriority();
 
242
 
 
243
    update_size = device->UpdateSize;
 
244
    while(!data->killNow)
 
245
    {
 
246
        hr = IAudioClient_GetCurrentPadding(data->client, &written);
 
247
        if(FAILED(hr))
 
248
        {
 
249
            ERR("Failed to get padding: 0x%08lx\n", hr);
 
250
            aluHandleDisconnect(device);
 
251
            break;
 
252
        }
 
253
 
 
254
        len = buffer_len - written;
 
255
        if(len < update_size)
 
256
        {
 
257
            DWORD res;
 
258
            res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE);
 
259
            if(res != WAIT_OBJECT_0)
 
260
                ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
 
261
            continue;
 
262
        }
 
263
        len -= len%update_size;
 
264
 
 
265
        hr = IAudioRenderClient_GetBuffer(data->render, len, &buffer);
 
266
        if(SUCCEEDED(hr))
 
267
        {
 
268
            aluMixData(device, buffer, len);
 
269
            hr = IAudioRenderClient_ReleaseBuffer(data->render, len, 0);
 
270
        }
 
271
        if(FAILED(hr))
 
272
        {
 
273
            ERR("Failed to buffer data: 0x%08lx\n", hr);
 
274
            aluHandleDisconnect(device);
 
275
            break;
 
276
        }
 
277
    }
 
278
 
 
279
    CoUninitialize();
 
280
    return 0;
 
281
}
 
282
 
 
283
 
 
284
static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
 
285
{
 
286
    memset(out, 0, sizeof(*out));
 
287
    if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
 
288
        *out = *(const WAVEFORMATEXTENSIBLE*)in;
 
289
    else if(in->wFormatTag == WAVE_FORMAT_PCM)
 
290
    {
 
291
        out->Format = *in;
 
292
        out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
 
293
        out->Format.cbSize = sizeof(*out) - sizeof(*in);
 
294
        if(out->Format.nChannels == 1)
 
295
            out->dwChannelMask = MONO;
 
296
        else if(out->Format.nChannels == 2)
 
297
            out->dwChannelMask = STEREO;
 
298
        else
 
299
            ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
 
300
        out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
301
    }
 
302
    else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
 
303
    {
 
304
        out->Format = *in;
 
305
        out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
 
306
        out->Format.cbSize = sizeof(*out) - sizeof(*in);
 
307
        if(out->Format.nChannels == 1)
 
308
            out->dwChannelMask = MONO;
 
309
        else if(out->Format.nChannels == 2)
 
310
            out->dwChannelMask = STEREO;
 
311
        else
 
312
            ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
 
313
        out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
 
314
    }
 
315
    else
 
316
    {
 
317
        ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
 
318
        return ALC_FALSE;
 
319
    }
 
320
    return ALC_TRUE;
 
321
}
 
322
 
 
323
static HRESULT DoReset(ALCdevice *device)
 
324
{
 
325
    MMDevApiData *data = device->ExtraData;
 
326
    WAVEFORMATEXTENSIBLE OutputType;
 
327
    WAVEFORMATEX *wfx = NULL;
 
328
    REFERENCE_TIME min_per, buf_time;
 
329
    UINT32 buffer_len, min_len;
 
330
    HRESULT hr;
 
331
 
 
332
    hr = IAudioClient_GetMixFormat(data->client, &wfx);
 
333
    if(FAILED(hr))
 
334
    {
 
335
        ERR("Failed to get mix format: 0x%08lx\n", hr);
 
336
        return hr;
 
337
    }
 
338
 
 
339
    if(!MakeExtensible(&OutputType, wfx))
 
340
    {
 
341
        CoTaskMemFree(wfx);
 
342
        return E_FAIL;
 
343
    }
 
344
    CoTaskMemFree(wfx);
 
345
    wfx = NULL;
 
346
 
 
347
    buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
 
348
                                device->Frequency-1) / device->Frequency;
 
349
 
 
350
    if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
 
351
        device->Frequency = OutputType.Format.nSamplesPerSec;
 
352
    if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
 
353
    {
 
354
        if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
 
355
            device->FmtChans = DevFmtMono;
 
356
        else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
 
357
            device->FmtChans = DevFmtStereo;
 
358
        else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
 
359
            device->FmtChans = DevFmtQuad;
 
360
        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
 
361
            device->FmtChans = DevFmtX51;
 
362
        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
 
363
            device->FmtChans = DevFmtX51Side;
 
364
        else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
 
365
            device->FmtChans = DevFmtX61;
 
366
        else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
 
367
            device->FmtChans = DevFmtX71;
 
368
        else
 
369
            ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
 
370
    }
 
371
 
 
372
    switch(device->FmtChans)
 
373
    {
 
374
        case DevFmtMono:
 
375
            OutputType.Format.nChannels = 1;
 
376
            OutputType.dwChannelMask = MONO;
 
377
            break;
 
378
        case DevFmtStereo:
 
379
            OutputType.Format.nChannels = 2;
 
380
            OutputType.dwChannelMask = STEREO;
 
381
            break;
 
382
        case DevFmtQuad:
 
383
            OutputType.Format.nChannels = 4;
 
384
            OutputType.dwChannelMask = QUAD;
 
385
            break;
 
386
        case DevFmtX51:
 
387
            OutputType.Format.nChannels = 6;
 
388
            OutputType.dwChannelMask = X5DOT1;
 
389
            break;
 
390
        case DevFmtX51Side:
 
391
            OutputType.Format.nChannels = 6;
 
392
            OutputType.dwChannelMask = X5DOT1SIDE;
 
393
            break;
 
394
        case DevFmtX61:
 
395
            OutputType.Format.nChannels = 7;
 
396
            OutputType.dwChannelMask = X6DOT1;
 
397
            break;
 
398
        case DevFmtX71:
 
399
            OutputType.Format.nChannels = 8;
 
400
            OutputType.dwChannelMask = X7DOT1;
 
401
            break;
 
402
    }
 
403
    switch(device->FmtType)
 
404
    {
 
405
        case DevFmtByte:
 
406
            device->FmtType = DevFmtUByte;
 
407
            /* fall-through */
 
408
        case DevFmtUByte:
 
409
            OutputType.Format.wBitsPerSample = 8;
 
410
            OutputType.Samples.wValidBitsPerSample = 8;
 
411
            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
412
            break;
 
413
        case DevFmtUShort:
 
414
            device->FmtType = DevFmtShort;
 
415
            /* fall-through */
 
416
        case DevFmtShort:
 
417
            OutputType.Format.wBitsPerSample = 16;
 
418
            OutputType.Samples.wValidBitsPerSample = 16;
 
419
            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
420
            break;
 
421
        case DevFmtUInt:
 
422
            device->FmtType = DevFmtInt;
 
423
            /* fall-through */
 
424
        case DevFmtInt:
 
425
            OutputType.Format.wBitsPerSample = 32;
 
426
            OutputType.Samples.wValidBitsPerSample = 32;
 
427
            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
428
            break;
 
429
        case DevFmtFloat:
 
430
            OutputType.Format.wBitsPerSample = 32;
 
431
            OutputType.Samples.wValidBitsPerSample = 32;
 
432
            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
 
433
            break;
 
434
    }
 
435
    OutputType.Format.nSamplesPerSec = device->Frequency;
 
436
 
 
437
    OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
 
438
                                    OutputType.Format.wBitsPerSample / 8;
 
439
    OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
 
440
                                        OutputType.Format.nBlockAlign;
 
441
 
 
442
    hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
 
443
    if(FAILED(hr))
 
444
    {
 
445
        ERR("Failed to check format support: 0x%08lx\n", hr);
 
446
        hr = IAudioClient_GetMixFormat(data->client, &wfx);
 
447
    }
 
448
    if(FAILED(hr))
 
449
    {
 
450
        ERR("Failed to find a supported format: 0x%08lx\n", hr);
 
451
        return hr;
 
452
    }
 
453
 
 
454
    if(wfx != NULL)
 
455
    {
 
456
        if(!MakeExtensible(&OutputType, wfx))
 
457
        {
 
458
            CoTaskMemFree(wfx);
 
459
            return E_FAIL;
 
460
        }
 
461
        CoTaskMemFree(wfx);
 
462
        wfx = NULL;
 
463
 
 
464
        device->Frequency = OutputType.Format.nSamplesPerSec;
 
465
        if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
 
466
            device->FmtChans = DevFmtMono;
 
467
        else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
 
468
            device->FmtChans = DevFmtStereo;
 
469
        else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
 
470
            device->FmtChans = DevFmtQuad;
 
471
        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
 
472
            device->FmtChans = DevFmtX51;
 
473
        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
 
474
            device->FmtChans = DevFmtX51Side;
 
475
        else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
 
476
            device->FmtChans = DevFmtX61;
 
477
        else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
 
478
            device->FmtChans = DevFmtX71;
 
479
        else
 
480
        {
 
481
            ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
 
482
            device->FmtChans = DevFmtStereo;
 
483
            OutputType.Format.nChannels = 2;
 
484
            OutputType.dwChannelMask = STEREO;
 
485
        }
 
486
 
 
487
        if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
 
488
        {
 
489
            if(OutputType.Format.wBitsPerSample == 8)
 
490
                device->FmtType = DevFmtUByte;
 
491
            else if(OutputType.Format.wBitsPerSample == 16)
 
492
                device->FmtType = DevFmtShort;
 
493
            else if(OutputType.Format.wBitsPerSample == 32)
 
494
                device->FmtType = DevFmtInt;
 
495
            else
 
496
            {
 
497
                device->FmtType = DevFmtShort;
 
498
                OutputType.Format.wBitsPerSample = 16;
 
499
            }
 
500
        }
 
501
        else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
 
502
        {
 
503
            device->FmtType = DevFmtFloat;
 
504
            OutputType.Format.wBitsPerSample = 32;
 
505
        }
 
506
        else
 
507
        {
 
508
            ERR("Unhandled format sub-type\n");
 
509
            device->FmtType = DevFmtShort;
 
510
            OutputType.Format.wBitsPerSample = 16;
 
511
            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
512
        }
 
513
        OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
 
514
    }
 
515
 
 
516
    SetDefaultWFXChannelOrder(device);
 
517
 
 
518
    hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
 
519
                                 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
 
520
                                 buf_time, 0, &OutputType.Format, NULL);
 
521
    if(FAILED(hr))
 
522
    {
 
523
        ERR("Failed to initialize audio client: 0x%08lx\n", hr);
 
524
        return hr;
 
525
    }
 
526
 
 
527
    hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
 
528
    if(SUCCEEDED(hr))
 
529
    {
 
530
        min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
 
531
        /* Find the nearest multiple of the period size to the update size */
 
532
        if(min_len < device->UpdateSize)
 
533
            min_len *= (device->UpdateSize + min_len/2)/min_len;
 
534
        hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
 
535
    }
 
536
    if(FAILED(hr))
 
537
    {
 
538
        ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
 
539
        return hr;
 
540
    }
 
541
 
 
542
    device->UpdateSize = min_len;
 
543
    device->NumUpdates = buffer_len / device->UpdateSize;
 
544
    if(device->NumUpdates <= 1)
 
545
    {
 
546
        ERR("Audio client returned buffer_len < period*2; expect break up\n");
 
547
        device->NumUpdates = 2;
 
548
        device->UpdateSize = buffer_len / device->NumUpdates;
 
549
    }
 
550
 
 
551
    return hr;
 
552
}
 
553
 
 
554
 
 
555
static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
 
556
{
 
557
    ThreadRequest *req = ptr;
 
558
    IMMDeviceEnumerator *Enumerator;
 
559
    ALuint deviceCount = 0;
 
560
    MMDevApiData *data;
 
561
    ALCdevice *device;
 
562
    HRESULT hr, cohr;
 
563
    MSG msg;
 
564
 
 
565
    TRACE("Starting message thread\n");
 
566
 
 
567
    cohr = CoInitialize(NULL);
 
568
    if(FAILED(cohr))
 
569
    {
 
570
        WARN("Failed to initialize COM: 0x%08lx\n", cohr);
 
571
        req->result = cohr;
 
572
        SetEvent(req->FinishedEvt);
 
573
        return 0;
 
574
    }
 
575
 
 
576
    hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
 
577
    if(FAILED(hr))
 
578
    {
 
579
        WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
 
580
        CoUninitialize();
 
581
        req->result = hr;
 
582
        SetEvent(req->FinishedEvt);
 
583
        return 0;
 
584
    }
 
585
    Enumerator = ptr;
 
586
    IMMDeviceEnumerator_Release(Enumerator);
 
587
    Enumerator = NULL;
 
588
 
 
589
    CoUninitialize();
 
590
 
 
591
    req->result = S_OK;
 
592
    SetEvent(req->FinishedEvt);
 
593
 
 
594
    TRACE("Starting message loop\n");
 
595
    while(GetMessage(&msg, NULL, 0, 0))
 
596
    {
 
597
        TRACE("Got message %u\n", msg.message);
 
598
        switch(msg.message)
 
599
        {
 
600
        case WM_USER_OpenDevice:
 
601
            req = (ThreadRequest*)msg.wParam;
 
602
            device = (ALCdevice*)msg.lParam;
 
603
            data = device->ExtraData;
 
604
 
 
605
            hr = cohr = S_OK;
 
606
            if(++deviceCount == 1)
 
607
                hr = cohr = CoInitialize(NULL);
 
608
            if(SUCCEEDED(hr))
 
609
                hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
 
610
            if(SUCCEEDED(hr))
 
611
            {
 
612
                Enumerator = ptr;
 
613
                if(!data->devid)
 
614
                    hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
 
615
                else
 
616
                    hr = IMMDeviceEnumerator_GetDevice(Enumerator, data->devid, &data->mmdev);
 
617
                IMMDeviceEnumerator_Release(Enumerator);
 
618
                Enumerator = NULL;
 
619
            }
 
620
            if(SUCCEEDED(hr))
 
621
                hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
 
622
            if(SUCCEEDED(hr))
 
623
            {
 
624
                data->client = ptr;
 
625
                device->szDeviceName = get_device_name(data->mmdev);
 
626
            }
 
627
 
 
628
            if(FAILED(hr))
 
629
            {
 
630
                if(data->mmdev)
 
631
                    IMMDevice_Release(data->mmdev);
 
632
                data->mmdev = NULL;
 
633
                if(--deviceCount == 0 && SUCCEEDED(cohr))
 
634
                    CoUninitialize();
 
635
            }
 
636
 
 
637
            req->result = hr;
 
638
            SetEvent(req->FinishedEvt);
 
639
            continue;
 
640
 
 
641
        case WM_USER_ResetDevice:
 
642
            req = (ThreadRequest*)msg.wParam;
 
643
            device = (ALCdevice*)msg.lParam;
 
644
 
 
645
            req->result = DoReset(device);
 
646
            SetEvent(req->FinishedEvt);
 
647
            continue;
 
648
 
 
649
        case WM_USER_StartDevice:
 
650
            req = (ThreadRequest*)msg.wParam;
 
651
            device = (ALCdevice*)msg.lParam;
 
652
            data = device->ExtraData;
 
653
 
 
654
            ResetEvent(data->hNotifyEvent);
 
655
            hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent);
 
656
            if(FAILED(hr))
 
657
                ERR("Failed to set event handle: 0x%08lx\n", hr);
 
658
            else
 
659
            {
 
660
                hr = IAudioClient_Start(data->client);
 
661
                if(FAILED(hr))
 
662
                    ERR("Failed to start audio client: 0x%08lx\n", hr);
 
663
            }
 
664
 
 
665
            if(SUCCEEDED(hr))
 
666
                hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &ptr);
 
667
            if(SUCCEEDED(hr))
 
668
            {
 
669
                data->render = ptr;
 
670
                data->thread = StartThread(MMDevApiProc, device);
 
671
                if(!data->thread)
 
672
                {
 
673
                    if(data->render)
 
674
                        IAudioRenderClient_Release(data->render);
 
675
                    data->render = NULL;
 
676
                    IAudioClient_Stop(data->client);
 
677
                    ERR("Failed to start thread\n");
 
678
                    hr = E_FAIL;
 
679
                }
 
680
            }
 
681
 
 
682
            req->result = hr;
 
683
            SetEvent(req->FinishedEvt);
 
684
            continue;
 
685
 
 
686
        case WM_USER_StopDevice:
 
687
            req = (ThreadRequest*)msg.wParam;
 
688
            device = (ALCdevice*)msg.lParam;
 
689
            data = device->ExtraData;
 
690
 
 
691
            if(data->thread)
 
692
            {
 
693
                data->killNow = 1;
 
694
                StopThread(data->thread);
 
695
                data->thread = NULL;
 
696
 
 
697
                data->killNow = 0;
 
698
 
 
699
                IAudioRenderClient_Release(data->render);
 
700
                data->render = NULL;
 
701
                IAudioClient_Stop(data->client);
 
702
            }
 
703
 
 
704
            req->result = S_OK;
 
705
            SetEvent(req->FinishedEvt);
 
706
            continue;
 
707
 
 
708
        case WM_USER_CloseDevice:
 
709
            req = (ThreadRequest*)msg.wParam;
 
710
            device = (ALCdevice*)msg.lParam;
 
711
            data = device->ExtraData;
 
712
 
 
713
            IAudioClient_Release(data->client);
 
714
            data->client = NULL;
 
715
 
 
716
            IMMDevice_Release(data->mmdev);
 
717
            data->mmdev = NULL;
 
718
 
 
719
            if(--deviceCount == 0)
 
720
                CoUninitialize();
 
721
 
 
722
            req->result = S_OK;
 
723
            SetEvent(req->FinishedEvt);
 
724
            continue;
 
725
 
 
726
        case WM_USER_Enumerate:
 
727
            req = (ThreadRequest*)msg.wParam;
 
728
 
 
729
            hr = cohr = S_OK;
 
730
            if(++deviceCount == 1)
 
731
                hr = cohr = CoInitialize(NULL);
 
732
            if(SUCCEEDED(hr))
 
733
                hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
 
734
            if(SUCCEEDED(hr))
 
735
            {
 
736
                EDataFlow flowdir;
 
737
                DevMap **devlist;
 
738
                ALuint *numdevs;
 
739
                ALuint i;
 
740
 
 
741
                Enumerator = ptr;
 
742
                if(msg.lParam == CAPTURE_DEVICE_PROBE)
 
743
                {
 
744
                    flowdir = eCapture;
 
745
                    devlist = &CaptureDeviceList;
 
746
                    numdevs = &NumCaptureDevices;
 
747
                }
 
748
                else
 
749
                {
 
750
                    flowdir = eRender;
 
751
                    devlist = &PlaybackDeviceList;
 
752
                    numdevs = &NumPlaybackDevices;
 
753
                }
 
754
 
 
755
                for(i = 0;i < *numdevs;i++)
 
756
                {
 
757
                    free((*devlist)[i].name);
 
758
                    free((*devlist)[i].devid);
 
759
                }
 
760
                free(*devlist);
 
761
                *devlist = NULL;
 
762
                *numdevs = 0;
 
763
 
 
764
                *devlist = ProbeDevices(Enumerator, flowdir, numdevs);
 
765
 
 
766
                IMMDeviceEnumerator_Release(Enumerator);
 
767
                Enumerator = NULL;
 
768
            }
 
769
 
 
770
            if(--deviceCount == 0 && SUCCEEDED(cohr))
 
771
                CoUninitialize();
 
772
 
 
773
            req->result = S_OK;
 
774
            SetEvent(req->FinishedEvt);
 
775
            continue;
 
776
 
 
777
        default:
 
778
            ERR("Unexpected message: %u\n", msg.message);
 
779
            continue;
 
780
        }
 
781
    }
 
782
    TRACE("Message loop finished\n");
 
783
 
 
784
    return 0;
 
785
}
 
786
 
 
787
 
 
788
static BOOL MMDevApiLoad(void)
 
789
{
 
790
    static HRESULT InitResult;
 
791
    if(!ThreadHdl)
 
792
    {
 
793
        ThreadRequest req;
 
794
        InitResult = E_FAIL;
 
795
 
 
796
        req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
 
797
        if(req.FinishedEvt == NULL)
 
798
            ERR("Failed to create event: %lu\n", GetLastError());
 
799
        else
 
800
        {
 
801
            ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
 
802
            if(ThreadHdl != NULL)
 
803
                InitResult = WaitForResponse(&req);
 
804
            CloseHandle(req.FinishedEvt);
 
805
        }
 
806
    }
 
807
    return SUCCEEDED(InitResult);
 
808
}
 
809
 
 
810
 
 
811
static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
 
812
{
 
813
    MMDevApiData *data = NULL;
 
814
    HRESULT hr;
 
815
 
 
816
    //Initialise requested device
 
817
    data = calloc(1, sizeof(MMDevApiData));
 
818
    if(!data)
 
819
        return ALC_OUT_OF_MEMORY;
 
820
    device->ExtraData = data;
 
821
 
 
822
    hr = S_OK;
 
823
    data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 
824
    data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 
825
    if(data->hNotifyEvent == NULL || data->MsgEvent == NULL)
 
826
        hr = E_FAIL;
 
827
 
 
828
    if(SUCCEEDED(hr))
 
829
    {
 
830
        if(deviceName)
 
831
        {
 
832
            ALuint i;
 
833
 
 
834
            if(!PlaybackDeviceList)
 
835
            {
 
836
                ThreadRequest req = { data->MsgEvent, 0 };
 
837
                if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
 
838
                    (void)WaitForResponse(&req);
 
839
            }
 
840
 
 
841
            hr = E_FAIL;
 
842
            for(i = 0;i < NumPlaybackDevices;i++)
 
843
            {
 
844
                if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
 
845
                {
 
846
                    data->devid = strdupW(PlaybackDeviceList[i].devid);
 
847
                    hr = S_OK;
 
848
                    break;
 
849
                }
 
850
            }
 
851
        }
 
852
    }
 
853
 
 
854
    if(SUCCEEDED(hr))
 
855
    {
 
856
        ThreadRequest req = { data->MsgEvent, 0 };
 
857
 
 
858
        hr = E_FAIL;
 
859
        if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
 
860
            hr = WaitForResponse(&req);
 
861
    }
 
862
 
 
863
    if(FAILED(hr))
 
864
    {
 
865
        if(data->hNotifyEvent != NULL)
 
866
            CloseHandle(data->hNotifyEvent);
 
867
        data->hNotifyEvent = NULL;
 
868
        if(data->MsgEvent != NULL)
 
869
            CloseHandle(data->MsgEvent);
 
870
        data->MsgEvent = NULL;
 
871
 
 
872
        free(data);
 
873
        device->ExtraData = NULL;
 
874
 
 
875
        ERR("Device init failed: 0x%08lx\n", hr);
 
876
        return ALC_INVALID_VALUE;
 
877
    }
 
878
 
 
879
    return ALC_NO_ERROR;
 
880
}
 
881
 
 
882
static void MMDevApiClosePlayback(ALCdevice *device)
 
883
{
 
884
    MMDevApiData *data = device->ExtraData;
 
885
    ThreadRequest req = { data->MsgEvent, 0 };
 
886
 
 
887
    if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
 
888
        (void)WaitForResponse(&req);
 
889
 
 
890
    CloseHandle(data->MsgEvent);
 
891
    data->MsgEvent = NULL;
 
892
 
 
893
    CloseHandle(data->hNotifyEvent);
 
894
    data->hNotifyEvent = NULL;
 
895
 
 
896
    free(data->devid);
 
897
    data->devid = NULL;
 
898
 
 
899
    free(data);
 
900
    device->ExtraData = NULL;
 
901
}
 
902
 
 
903
static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
 
904
{
 
905
    MMDevApiData *data = device->ExtraData;
 
906
    ThreadRequest req = { data->MsgEvent, 0 };
 
907
    HRESULT hr = E_FAIL;
 
908
 
 
909
    if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
 
910
        hr = WaitForResponse(&req);
 
911
 
 
912
    return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
 
913
}
 
914
 
 
915
static ALCboolean MMDevApiStartPlayback(ALCdevice *device)
 
916
{
 
917
    MMDevApiData *data = device->ExtraData;
 
918
    ThreadRequest req = { data->MsgEvent, 0 };
 
919
    HRESULT hr = E_FAIL;
 
920
 
 
921
    if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)device))
 
922
        hr = WaitForResponse(&req);
 
923
 
 
924
    return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
 
925
}
 
926
 
 
927
static void MMDevApiStopPlayback(ALCdevice *device)
 
928
{
 
929
    MMDevApiData *data = device->ExtraData;
 
930
    ThreadRequest req = { data->MsgEvent, 0 };
 
931
 
 
932
    if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
 
933
        (void)WaitForResponse(&req);
 
934
}
 
935
 
 
936
 
 
937
static const BackendFuncs MMDevApiFuncs = {
 
938
    MMDevApiOpenPlayback,
 
939
    MMDevApiClosePlayback,
 
940
    MMDevApiResetPlayback,
 
941
    MMDevApiStartPlayback,
 
942
    MMDevApiStopPlayback,
 
943
    NULL,
 
944
    NULL,
 
945
    NULL,
 
946
    NULL,
 
947
    NULL,
 
948
    NULL
 
949
};
 
950
 
 
951
 
 
952
ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
 
953
{
 
954
    if(!MMDevApiLoad())
 
955
        return ALC_FALSE;
 
956
    *FuncList = MMDevApiFuncs;
 
957
    return ALC_TRUE;
 
958
}
 
959
 
 
960
void alcMMDevApiDeinit(void)
 
961
{
 
962
    ALuint i;
 
963
 
 
964
    for(i = 0;i < NumPlaybackDevices;i++)
 
965
    {
 
966
        free(PlaybackDeviceList[i].name);
 
967
        free(PlaybackDeviceList[i].devid);
 
968
    }
 
969
    free(PlaybackDeviceList);
 
970
    PlaybackDeviceList = NULL;
 
971
    NumPlaybackDevices = 0;
 
972
 
 
973
    for(i = 0;i < NumCaptureDevices;i++)
 
974
    {
 
975
        free(CaptureDeviceList[i].name);
 
976
        free(CaptureDeviceList[i].devid);
 
977
    }
 
978
    free(CaptureDeviceList);
 
979
    CaptureDeviceList = NULL;
 
980
    NumCaptureDevices = 0;
 
981
 
 
982
    if(ThreadHdl)
 
983
    {
 
984
        TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
 
985
        PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
 
986
        CloseHandle(ThreadHdl);
 
987
        ThreadHdl = NULL;
 
988
    }
 
989
}
 
990
 
 
991
void alcMMDevApiProbe(enum DevProbe type)
 
992
{
 
993
    ThreadRequest req = { NULL, 0 };
 
994
    HRESULT hr = E_FAIL;
 
995
 
 
996
    switch(type)
 
997
    {
 
998
        case ALL_DEVICE_PROBE:
 
999
            req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
 
1000
            if(req.FinishedEvt == NULL)
 
1001
                ERR("Failed to create event: %lu\n", GetLastError());
 
1002
            else if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
 
1003
                hr = WaitForResponse(&req);
 
1004
            if(SUCCEEDED(hr))
 
1005
            {
 
1006
                ALuint i;
 
1007
                for(i = 0;i < NumPlaybackDevices;i++)
 
1008
                {
 
1009
                    if(PlaybackDeviceList[i].name)
 
1010
                        AppendAllDeviceList(PlaybackDeviceList[i].name);
 
1011
                }
 
1012
            }
 
1013
            break;
 
1014
 
 
1015
        case CAPTURE_DEVICE_PROBE:
 
1016
            break;
 
1017
    }
 
1018
    if(req.FinishedEvt != NULL)
 
1019
        CloseHandle(req.FinishedEvt);
 
1020
    req.FinishedEvt = NULL;
 
1021
}