Merge pull request #2200 from MartijnKaijser/header_update
[vuplus_xbmc] / xbmc / cores / AudioEngine / Sinks / AESinkDirectSound.cpp
1 /*
2  *      Copyright (C) 2010-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #define INITGUID
22
23 #include "AESinkDirectSound.h"
24 #include "utils/Log.h"
25 #include <initguid.h>
26 #include <Mmreg.h>
27 #include <list>
28 #include "threads/SingleLock.h"
29 #include "utils/SystemInfo.h"
30 #include "utils/TimeUtils.h"
31 #include "utils/CharsetConverter.h"
32 #include <Audioclient.h>
33 #include <Mmreg.h>
34 #include <mmdeviceapi.h>
35 #include <Functiondiscoverykeys_devpkey.h>
36 #include <Rpc.h>
37 #include "cores/AudioEngine/Utils/AEUtil.h"
38 #pragma comment(lib, "Rpcrt4.lib")
39
40 extern HWND g_hWnd;
41
42 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
43 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
44
45 #define EXIT_ON_FAILURE(hr, reason, ...) if(FAILED(hr)) {CLog::Log(LOGERROR, reason " - %s", __VA_ARGS__, WASAPIErrToStr(hr)); goto failed;}
46
47 #define ERRTOSTR(err) case err: return #err
48
49 #define DS_SPEAKER_COUNT 8
50 static const unsigned int DSChannelOrder[] = {SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT, SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT};
51 static const enum AEChannel AEChannelNames[] = {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_LFE, AE_CH_BL, AE_CH_BR, AE_CH_SL, AE_CH_SR, AE_CH_NULL};
52
53 static enum AEChannel layoutsByChCount[9][9] = {
54     {AE_CH_NULL},
55     {AE_CH_FC, AE_CH_NULL},
56     {AE_CH_FL, AE_CH_FR, AE_CH_NULL},
57     {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_NULL},
58     {AE_CH_FL, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
59     {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
60     {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_LFE, AE_CH_NULL},
61     {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_BC, AE_CH_LFE, AE_CH_NULL},
62     {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_BL, AE_CH_BR, AE_CH_SL, AE_CH_SR, AE_CH_LFE, AE_CH_NULL}};
63
64 struct DSDevice
65 {
66   std::string name;
67   LPGUID     lpGuid;
68 };
69
70 struct winEndpointsToAEDeviceType
71 {
72   std::string winEndpointType;
73   AEDeviceType aeDeviceType;
74 };
75
76 static const winEndpointsToAEDeviceType winEndpoints[EndpointFormFactor_enum_count] =
77 {
78   {"Network Device - ",         AE_DEVTYPE_PCM},
79   {"Speakers - ",               AE_DEVTYPE_PCM},
80   {"LineLevel - ",              AE_DEVTYPE_PCM},
81   {"Headphones - ",             AE_DEVTYPE_PCM},
82   {"Microphone - ",             AE_DEVTYPE_PCM},
83   {"Headset - ",                AE_DEVTYPE_PCM},
84   {"Handset - ",                AE_DEVTYPE_PCM},
85   {"Digital Passthrough - ", AE_DEVTYPE_IEC958},
86   {"SPDIF - ",               AE_DEVTYPE_IEC958},
87   {"HDMI - ",                  AE_DEVTYPE_HDMI},
88   {"Unknown - ",                AE_DEVTYPE_PCM},
89 };
90
91 // implemented in AESinkWASAPI.cpp
92 extern CStdStringA localWideToUtf(LPCWSTR wstr);
93
94 static BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
95 {
96   DSDevice dev;
97   std::list<DSDevice> &enumerator = *static_cast<std::list<DSDevice>*>(lpContext);
98
99   int bufSize = MultiByteToWideChar(CP_ACP, 0, lpcstrDescription, -1, NULL, 0);
100   CStdStringW strW (L"", bufSize);
101   if ( bufSize == 0 || MultiByteToWideChar(CP_ACP, 0, lpcstrDescription, -1, strW.GetBuf(bufSize), bufSize) != bufSize )
102     strW.clear();
103   strW.RelBuf();
104
105   dev.name = localWideToUtf(strW);
106
107   dev.lpGuid = lpGuid;
108
109   if (lpGuid)
110     enumerator.push_back(dev);
111
112   return TRUE;
113 }
114
115 CAESinkDirectSound::CAESinkDirectSound() :
116   m_initialized   (false),
117   m_isDirtyDS     (false),
118   m_pBuffer       (NULL ),
119   m_pDSound       (NULL ),
120   m_BufferOffset  (0    ),
121   m_CacheLen      (0    ),
122   m_dwChunkSize   (0    ),
123   m_dwBufferLen   (0    ),
124   m_BufferTimeouts(0    )
125 {
126   m_channelLayout.Reset();
127 }
128
129 CAESinkDirectSound::~CAESinkDirectSound()
130 {
131   Deinitialize();
132 }
133
134 bool CAESinkDirectSound::Initialize(AEAudioFormat &format, std::string &device)
135 {
136   if (m_initialized)
137     return false;
138
139   LPGUID deviceGUID = NULL;
140   RPC_CSTR wszUuid  = NULL;
141   HRESULT hr = E_FAIL;
142   std::list<DSDevice> DSDeviceList;
143   std::string deviceFriendlyName;
144   DirectSoundEnumerate(DSEnumCallback, &DSDeviceList);
145
146   for (std::list<DSDevice>::iterator itt = DSDeviceList.begin(); itt != DSDeviceList.end(); itt++)
147   {
148     if ((*itt).lpGuid)
149     {
150       hr = (UuidToString((*itt).lpGuid, &wszUuid));
151       std::string sztmp = (char*)wszUuid;
152       std::string szGUID = "{" + std::string(sztmp.begin(), sztmp.end()) + "}";
153       if (strcasecmp(szGUID.c_str(), device.c_str()) == 0)
154       {
155         deviceGUID = (*itt).lpGuid;
156         deviceFriendlyName = (*itt).name.c_str();
157         break;
158       }
159     }
160   if (hr == RPC_S_OK) RpcStringFree(&wszUuid);
161   }
162
163   hr = DirectSoundCreate(deviceGUID, &m_pDSound, NULL);
164
165   if (FAILED(hr))
166   {
167     CLog::Log(LOGERROR, __FUNCTION__": Failed to create the DirectSound device.");
168     CLog::Log(LOGERROR, __FUNCTION__": DSErr: %s", dserr2str(hr));
169     return false;
170   }
171
172   HWND tmp_hWnd;
173
174   /* Dodge the null handle on first init by using desktop handle */
175   if (g_hWnd == NULL)
176     tmp_hWnd = GetDesktopWindow();
177   else
178     tmp_hWnd = g_hWnd;
179
180   CLog::Log(LOGDEBUG, __FUNCTION__": Using Window handle: %d", tmp_hWnd);
181
182   hr = m_pDSound->SetCooperativeLevel(tmp_hWnd, DSSCL_PRIORITY);
183
184   if (FAILED(hr))
185   {
186     CLog::Log(LOGERROR, __FUNCTION__": Failed to create the DirectSound device cooperative level.");
187     CLog::Log(LOGERROR, __FUNCTION__": DSErr: %s", dserr2str(hr));
188     m_pDSound->Release();
189     return false;
190   }
191
192   WAVEFORMATEXTENSIBLE wfxex = {0};
193
194   //fill waveformatex
195   ZeroMemory(&wfxex, sizeof(WAVEFORMATEXTENSIBLE));
196   wfxex.Format.cbSize          = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
197   wfxex.Format.nChannels       = format.m_channelLayout.Count();
198   wfxex.Format.nSamplesPerSec  = format.m_sampleRate;
199   if (AE_IS_RAW(format.m_dataFormat))
200   {
201     wfxex.dwChannelMask          = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
202     wfxex.Format.wFormatTag      = WAVE_FORMAT_DOLBY_AC3_SPDIF;
203     wfxex.SubFormat              = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
204     wfxex.Format.wBitsPerSample  = 16;
205     wfxex.Format.nChannels       = 2;
206   }
207   else
208   {
209     wfxex.dwChannelMask          = SpeakerMaskFromAEChannels(format.m_channelLayout);
210     wfxex.Format.wFormatTag      = WAVE_FORMAT_EXTENSIBLE;
211     wfxex.SubFormat              = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
212     wfxex.Format.wBitsPerSample  = 32;
213   }
214
215   wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample;
216   wfxex.Format.nBlockAlign          = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
217   wfxex.Format.nAvgBytesPerSec      = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;
218
219   m_AvgBytesPerSec = wfxex.Format.nAvgBytesPerSec;
220
221   unsigned int uiFrameCount = (int)(format.m_sampleRate * 0.01); //default to 10ms chunks
222   m_dwFrameSize = wfxex.Format.nBlockAlign;
223   m_dwChunkSize = m_dwFrameSize * uiFrameCount;
224   m_dwBufferLen = m_dwChunkSize * 12; //120ms total buffer
225
226   // fill in the secondary sound buffer descriptor
227   DSBUFFERDESC dsbdesc;
228   memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
229   dsbdesc.dwSize = sizeof(DSBUFFERDESC);
230   dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
231                   | DSBCAPS_GLOBALFOCUS;         /** Allows background playing */
232
233   if (!g_sysinfo.IsVistaOrHigher())
234     dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;     /** Needed for 5.1 on emu101k, fails by design on Vista */
235
236   dsbdesc.dwBufferBytes = m_dwBufferLen;
237   dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wfxex;
238
239   // now create the stream buffer
240   HRESULT res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
241   if (res != DS_OK)
242   {
243     if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE)
244     {
245       SAFE_RELEASE(m_pBuffer);
246       CLog::Log(LOGDEBUG, __FUNCTION__": Couldn't create secondary buffer (%s). Trying without LOCHARDWARE.", dserr2str(res));
247       // Try without DSBCAPS_LOCHARDWARE
248       dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
249       res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
250     }
251     if (res != DS_OK)
252     {
253       SAFE_RELEASE(m_pBuffer);
254       CLog::Log(LOGERROR, __FUNCTION__": cannot create secondary buffer (%s)", dserr2str(res));
255       return false;
256     }
257   }
258   CLog::Log(LOGDEBUG, __FUNCTION__": secondary buffer created");
259
260   m_pBuffer->Stop();
261
262   AEChannelsFromSpeakerMask(wfxex.dwChannelMask);
263   format.m_channelLayout = m_channelLayout;
264   format.m_frames = uiFrameCount;
265   format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
266   format.m_frameSize = (AE_IS_RAW(format.m_dataFormat) ? wfxex.Format.wBitsPerSample >> 3 : sizeof(float)) * format.m_channelLayout.Count();
267   format.m_dataFormat = AE_IS_RAW(format.m_dataFormat) ? AE_FMT_S16NE : AE_FMT_FLOAT;
268
269   m_format = format;
270   m_device = device;
271
272   m_BufferOffset = 0;
273   m_CacheLen = 0;
274   m_LastCacheCheck = XbmcThreads::SystemClockMillis();
275   m_initialized = true;
276   m_isDirtyDS = false;
277
278   CLog::Log(LOGDEBUG, __FUNCTION__": Initializing DirectSound with the following parameters:");
279   CLog::Log(LOGDEBUG, "  Audio Device    : %s", ((std::string)deviceFriendlyName).c_str());
280   CLog::Log(LOGDEBUG, "  Sample Rate     : %d", wfxex.Format.nSamplesPerSec);
281   CLog::Log(LOGDEBUG, "  Sample Format   : %s", CAEUtil::DataFormatToStr(format.m_dataFormat));
282   CLog::Log(LOGDEBUG, "  Bits Per Sample : %d", wfxex.Format.wBitsPerSample);
283   CLog::Log(LOGDEBUG, "  Valid Bits/Samp : %d", wfxex.Samples.wValidBitsPerSample);
284   CLog::Log(LOGDEBUG, "  Channel Count   : %d", wfxex.Format.nChannels);
285   CLog::Log(LOGDEBUG, "  Block Align     : %d", wfxex.Format.nBlockAlign);
286   CLog::Log(LOGDEBUG, "  Avg. Bytes Sec  : %d", wfxex.Format.nAvgBytesPerSec);
287   CLog::Log(LOGDEBUG, "  Samples/Block   : %d", wfxex.Samples.wSamplesPerBlock);
288   CLog::Log(LOGDEBUG, "  Format cBSize   : %d", wfxex.Format.cbSize);
289   CLog::Log(LOGDEBUG, "  Channel Layout  : %s", ((std::string)format.m_channelLayout).c_str());
290   CLog::Log(LOGDEBUG, "  Channel Mask    : %d", wfxex.dwChannelMask);
291   CLog::Log(LOGDEBUG, "  Frames          : %d", format.m_frames);
292   CLog::Log(LOGDEBUG, "  Frame Samples   : %d", format.m_frameSamples);
293   CLog::Log(LOGDEBUG, "  Frame Size      : %d", format.m_frameSize);
294
295   return true;
296 }
297
298 void CAESinkDirectSound::Deinitialize()
299 {
300   if (!m_initialized)
301     return;
302
303   CLog::Log(LOGDEBUG, __FUNCTION__": Cleaning up");
304
305   if (m_pBuffer)
306   {
307     m_pBuffer->Stop();
308     SAFE_RELEASE(m_pBuffer);
309   }
310
311   if (m_pDSound)
312   {
313     m_pDSound->Release();
314   }
315
316   m_initialized = false;
317   m_pBuffer = NULL;
318   m_pDSound = NULL;
319   m_BufferOffset = 0;
320   m_CacheLen = 0;
321   m_dwChunkSize = 0;
322   m_dwBufferLen = 0;
323 }
324
325 bool CAESinkDirectSound::IsCompatible(const AEAudioFormat format, const std::string device)
326 {
327   if (!m_initialized || m_isDirtyDS)
328     return false;
329
330   u_int notCompatible         = 0;
331   const u_int numTests        = 6;
332   std::string strDiffBecause ("");
333   static const char* compatibleParams[numTests] = {":Devices",
334                                                    ":Channels",
335                                                    ":Sample Rates",
336                                                    ":Data Formats",
337                                                    ":Bluray Formats",
338                                                    ":Passthrough Formats"};
339
340   notCompatible = (notCompatible  +!((AE_IS_RAW(format.m_dataFormat)  == AE_IS_RAW(m_encodedFormat))        ||
341                                      (!AE_IS_RAW(format.m_dataFormat) == !AE_IS_RAW(m_encodedFormat))))     << 1;
342   notCompatible = (notCompatible  + ((format.m_dataFormat             == AE_FMT_EAC3)                       ||
343                                      (format.m_dataFormat             == AE_FMT_DTSHD                       ||
344                                      (format.m_dataFormat             == AE_FMT_TRUEHD))))                  << 1;
345   notCompatible = (notCompatible  + !(format.m_dataFormat             == m_format.m_dataFormat))            << 1;
346   notCompatible = (notCompatible  + !(format.m_sampleRate             == m_format.m_sampleRate))            << 1;
347   notCompatible = (notCompatible  + !(format.m_channelLayout.Count()  == m_format.m_channelLayout.Count())) << 1;
348   notCompatible = (notCompatible  + !(m_device                        == device));
349
350   if (!notCompatible)
351   {
352     CLog::Log(LOGDEBUG, __FUNCTION__": Formats compatible - reusing existing sink");
353     return true;
354   }
355
356   for (int i = 0; i < numTests ; i++)
357   {
358     strDiffBecause += (notCompatible & 0x01) ? (std::string) compatibleParams[i] : "";
359     notCompatible    = notCompatible >> 1;
360   }
361
362   CLog::Log(LOGDEBUG, __FUNCTION__": Formats Incompatible due to different %s", strDiffBecause.c_str());
363   return false;
364 }
365
366 unsigned int CAESinkDirectSound::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
367 {
368   if (!m_initialized)
369     return 0;
370
371   DWORD total = m_dwFrameSize * frames;
372   DWORD len = total;
373   unsigned char* pBuffer = (unsigned char*)data;
374
375   DWORD bufferStatus = 0;
376   m_pBuffer->GetStatus(&bufferStatus);
377   if (bufferStatus & DSBSTATUS_BUFFERLOST)
378   {
379     CLog::Log(LOGDEBUG, __FUNCTION__ ": Buffer allocation was lost. Restoring buffer.");
380     m_pBuffer->Restore();
381   }
382
383   while (GetSpace() < total)
384   {
385     if (m_isDirtyDS)
386       return INT_MAX;
387     Sleep(total * 1000 / m_AvgBytesPerSec);
388   }
389
390   while (len)
391   {
392     LPVOID start = NULL, startWrap = NULL;
393     DWORD size = 0, sizeWrap = 0;
394     if (m_BufferOffset >= m_dwBufferLen) // Wrap-around manually
395       m_BufferOffset = 0;
396     HRESULT res = m_pBuffer->Lock(m_BufferOffset, m_dwChunkSize, &start, &size, &startWrap, &sizeWrap, 0);
397     if (DS_OK != res)
398     {
399       CLog::Log(LOGERROR, __FUNCTION__ ": Unable to lock buffer at offset %u. HRESULT: 0x%08x", m_BufferOffset, res);
400       m_isDirtyDS = true;
401       return INT_MAX;
402     }
403
404     memcpy(start, pBuffer, size);
405
406     pBuffer += size;
407     len     -= size;
408
409     m_BufferOffset += size;
410     if (startWrap) // Write-region wraps to beginning of buffer
411     {
412       memcpy(startWrap, pBuffer, sizeWrap);
413       m_BufferOffset = sizeWrap;
414
415       pBuffer += sizeWrap;
416       len     -= sizeWrap;
417     }
418
419     m_CacheLen += size + sizeWrap; // This data is now in the cache
420     m_pBuffer->Unlock(start, size, startWrap, sizeWrap);
421   }
422
423   CheckPlayStatus();
424
425   return (total - len) / m_dwFrameSize; // Frames used
426 }
427
428 void CAESinkDirectSound::Stop()
429 {
430   if (m_pBuffer)
431     m_pBuffer->Stop();
432 }
433
434 double CAESinkDirectSound::GetDelay()
435 {
436   if (!m_initialized)
437     return 0.0;
438
439   /* Make sure we know how much data is in the cache */
440   if (!UpdateCacheStatus())
441     m_isDirtyDS = true;
442
443   /** returns current cached data duration in seconds */
444   double delay = (double)m_CacheLen / (double)m_AvgBytesPerSec;
445   return delay;
446 }
447
448 double CAESinkDirectSound::GetCacheTime()
449 {
450   if (!m_initialized)
451     return 0.0;
452
453   /* Make sure we know how much data is in the cache */
454   UpdateCacheStatus();
455
456   /** returns current cached data duration in seconds */
457   double delay = (double)m_CacheLen / (double)m_AvgBytesPerSec;
458   return delay;
459 }
460
461 double CAESinkDirectSound::GetCacheTotal()
462 {
463   /** returns total cache capacity in seconds */
464   return (double)m_dwBufferLen / (double)m_AvgBytesPerSec;
465 }
466
467 void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force)
468 {
469   CAEDeviceInfo        deviceInfo;
470
471   IMMDeviceEnumerator* pEnumerator = NULL;
472   IMMDeviceCollection* pEnumDevices = NULL;
473
474   WAVEFORMATEX*          pwfxex = NULL;
475   HRESULT                hr;
476
477   /* See if we are on Windows XP */
478   if (!g_sysinfo.IsVistaOrHigher())
479   {
480     /* We are on XP - WASAPI not supported - enumerate using DS devices */
481     LPGUID deviceGUID = NULL;
482     RPC_CSTR cszGUID;
483     std::string szGUID;
484     std::list<DSDevice> DSDeviceList;
485     DirectSoundEnumerate(DSEnumCallback, &DSDeviceList);
486
487     for(std::list<DSDevice>::iterator itt = DSDeviceList.begin(); itt != DSDeviceList.end(); itt++)
488     {
489       if (UuidToString((*itt).lpGuid, &cszGUID) != RPC_S_OK)
490         continue;  /* could not convert GUID to string - skip device */
491
492       deviceInfo.m_channels.Reset();
493       deviceInfo.m_dataFormats.clear();
494       deviceInfo.m_sampleRates.clear();
495
496       szGUID = (LPSTR)cszGUID;
497
498       deviceInfo.m_deviceName = "{" + szGUID + "}";
499       deviceInfo.m_displayName = (*itt).name;
500       deviceInfo.m_displayNameExtra = std::string("DirectSound: ") + (*itt).name;
501
502       deviceInfo.m_deviceType = AE_DEVTYPE_PCM;
503       deviceInfo.m_channels   = layoutsByChCount[2];
504
505       deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT));
506       deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3));
507
508       deviceInfo.m_sampleRates.push_back((DWORD) 96000);
509
510       deviceInfoList.push_back(deviceInfo);
511     }
512
513     RpcStringFree(&cszGUID);
514     return;
515   }
516
517   /* Windows Vista or later - supporting WASAPI device probing */
518   hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
519   EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %li", hr)
520
521   UINT uiCount = 0;
522
523   hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices);
524   EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.")
525
526   hr = pEnumDevices->GetCount(&uiCount);
527   EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.")
528
529   for (UINT i = 0; i < uiCount; i++)
530   {
531     IMMDevice *pDevice = NULL;
532     IPropertyStore *pProperty = NULL;
533     PROPVARIANT varName;
534     PropVariantInit(&varName);
535
536     deviceInfo.m_channels.Reset();
537     deviceInfo.m_dataFormats.clear();
538     deviceInfo.m_sampleRates.clear();
539
540     hr = pEnumDevices->Item(i, &pDevice);
541     if (FAILED(hr))
542     {
543       CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint failed.");
544       goto failed;
545     }
546
547     hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty);
548     if (FAILED(hr))
549     {
550       CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint properties failed.");
551       SAFE_RELEASE(pDevice);
552       goto failed;
553     }
554
555     hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName);
556     if (FAILED(hr))
557     {
558       CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint device name failed.");
559       SAFE_RELEASE(pDevice);
560       SAFE_RELEASE(pProperty);
561       goto failed;
562     }
563
564     std::string strFriendlyName = localWideToUtf(varName.pwszVal);
565     PropVariantClear(&varName);
566
567     hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
568     if (FAILED(hr))
569     {
570       CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint GUID failed.");
571       SAFE_RELEASE(pDevice);
572       SAFE_RELEASE(pProperty);
573       goto failed;
574     }
575
576     std::string strDevName = localWideToUtf(varName.pwszVal);
577     PropVariantClear(&varName);
578
579     hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName);
580     if (FAILED(hr))
581     {
582       CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint form factor failed.");
583       SAFE_RELEASE(pDevice);
584       SAFE_RELEASE(pProperty);
585       goto failed;
586     }
587     std::string strWinDevType = winEndpoints[(EndpointFormFactor)varName.uiVal].winEndpointType;
588     AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType;
589
590     PropVariantClear(&varName);
591
592     /* In shared mode Windows tells us what format the audio must be in. */
593     IAudioClient *pClient;
594     hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pClient);
595     if (FAILED(hr))
596     {
597       CLog::Log(LOGERROR, __FUNCTION__": Activate device failed (%s)", WASAPIErrToStr(hr));
598       goto failed;
599     }
600
601     //hr = pClient->GetMixFormat(&pwfxex);
602     hr = pProperty->GetValue(PKEY_AudioEngine_DeviceFormat, &varName);
603     if (SUCCEEDED(hr) && varName.blob.cbSize > 0)
604     {
605       WAVEFORMATEX* smpwfxex = (WAVEFORMATEX*)varName.blob.pBlobData;
606       deviceInfo.m_channels = layoutsByChCount[std::min(smpwfxex->nChannels, (WORD) 2)];
607       deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT));
608       deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3));
609       deviceInfo.m_sampleRates.push_back(std::min(smpwfxex->nSamplesPerSec, (DWORD) 96000));
610     }
611     else
612     {
613       CLog::Log(LOGERROR, __FUNCTION__": Getting DeviceFormat failed (%s)", WASAPIErrToStr(hr));
614     }
615     pClient->Release();
616
617     SAFE_RELEASE(pDevice);
618     SAFE_RELEASE(pProperty);
619
620     deviceInfo.m_deviceName       = strDevName;
621     deviceInfo.m_displayName      = strWinDevType.append(strFriendlyName);
622     deviceInfo.m_displayNameExtra = std::string("DirectSound: ").append(strFriendlyName);
623     deviceInfo.m_deviceType       = aeDeviceType;
624
625     deviceInfoList.push_back(deviceInfo);
626   }
627
628   // since AE takes the first device in deviceInfoList as default audio device we need
629   // to sort it in order to use the real default device
630   if(deviceInfoList.size() > 1)
631   {
632     std::string strDD = GetDefaultDevice();
633     for (AEDeviceInfoList::iterator itt = deviceInfoList.begin(); itt != deviceInfoList.end(); ++itt)
634     {
635       CAEDeviceInfo devInfo = *itt;
636       if(devInfo.m_deviceName == strDD)
637       {
638         deviceInfoList.erase(itt);
639         deviceInfoList.insert(deviceInfoList.begin(), devInfo);
640       }
641     }
642   }
643
644   return;
645
646 failed:
647
648   if (FAILED(hr))
649     CLog::Log(LOGERROR, __FUNCTION__": Failed to enumerate WASAPI endpoint devices (%s).", WASAPIErrToStr(hr));
650
651   SAFE_RELEASE(pEnumDevices);
652   SAFE_RELEASE(pEnumerator);
653
654 }
655
656 ///////////////////////////////////////////////////////////////////////////////
657
658 void CAESinkDirectSound::CheckPlayStatus()
659 {
660   DWORD status = 0;
661   m_pBuffer->GetStatus(&status);
662
663   if (!(status & DSBSTATUS_PLAYING) && m_CacheLen != 0) // If we have some data, see if we can start playback
664   {
665     HRESULT hr = m_pBuffer->Play(0, 0, DSBPLAY_LOOPING);
666     CLog::Log(LOGDEBUG,__FUNCTION__ ": Resuming Playback");
667     if (FAILED(hr))
668       CLog::Log(LOGERROR, __FUNCTION__": Failed to play the DirectSound buffer: %s", dserr2str(hr));
669   }
670 }
671
672 bool CAESinkDirectSound::UpdateCacheStatus()
673 {
674   CSingleLock lock (m_runLock);
675   // TODO: Check to see if we may have cycled around since last time
676   unsigned int time = XbmcThreads::SystemClockMillis();
677   if (time == m_LastCacheCheck)
678     return true; // Don't recalc more frequently than once/ms (that is our max resolution anyway)
679
680   DWORD playCursor = 0, writeCursor = 0;
681   HRESULT res = m_pBuffer->GetCurrentPosition(&playCursor, &writeCursor); // Get the current playback and safe write positions
682   if (DS_OK != res)
683   {
684     CLog::Log(LOGERROR,__FUNCTION__ ": GetCurrentPosition failed. Unable to determine buffer status. HRESULT = 0x%08x", res);
685     m_isDirtyDS = true;
686     return false;
687   }
688
689   m_LastCacheCheck = time;
690   // Check the state of the ring buffer (P->O->W == underrun)
691   // These are the logical situations that can occur
692   // O: CurrentOffset  W: WriteCursor  P: PlayCursor
693   // | | | | | | | | | |
694   // ***O----W----P***** < underrun   P > W && O < W (1)
695   // | | | | | | | | | |
696   // ---P****O----W----- < underrun   O > P && O < W (2)
697   // | | | | | | | | | |
698   // ---W----P****O----- < underrun   P > W && P < O (3)
699   // | | | | | | | | | |
700   // ***W****O----P*****              P > W && P > O (4)
701   // | | | | | | | | | |
702   // ---P****W****O-----              P < W && O > W (5)
703   // | | | | | | | | | |
704   // ***O----P****W*****              P < W && O < P (6)
705
706   // Check for underruns
707   if ((playCursor > writeCursor && m_BufferOffset < writeCursor) ||    // (1)
708       (playCursor < m_BufferOffset && m_BufferOffset < writeCursor) || // (2)
709       (playCursor > writeCursor && playCursor <  m_BufferOffset))      // (3)
710   {
711     CLog::Log(LOGWARNING, "CWin32DirectSound::GetSpace - buffer underrun - W:%u, P:%u, O:%u.", writeCursor, playCursor, m_BufferOffset);
712     m_BufferOffset = writeCursor; // Catch up
713     //m_pBuffer->Stop(); // Wait until someone gives us some data to restart playback (prevents glitches)
714     m_BufferTimeouts++;
715     if (m_BufferTimeouts > 10)
716     {
717       m_isDirtyDS = true;
718       return false;
719     }
720   }
721   else m_BufferTimeouts = 0;
722
723   // Calculate available space in the ring buffer
724   if (playCursor == m_BufferOffset && m_BufferOffset ==  writeCursor) // Playback is stopped and we are all at the same place
725     m_CacheLen = 0;
726   else if (m_BufferOffset > playCursor)
727     m_CacheLen = m_BufferOffset - playCursor;
728   else
729     m_CacheLen = m_dwBufferLen - (playCursor - m_BufferOffset);
730
731   return true;
732 }
733
734 unsigned int CAESinkDirectSound::GetSpace()
735 {
736   CSingleLock lock (m_runLock);
737   if (!UpdateCacheStatus())
738     m_isDirtyDS = true;
739   unsigned int space = m_dwBufferLen - m_CacheLen;
740
741   // We can never allow the internal buffers to fill up complete
742   // as we get confused between if the buffer is full or empty
743   // so never allow the last chunk to be added
744   if (space > m_dwChunkSize)
745     return space - m_dwChunkSize;
746   else
747     return 0;
748 }
749
750 void CAESinkDirectSound::AEChannelsFromSpeakerMask(DWORD speakers)
751 {
752   int j = 0;
753
754   m_channelLayout.Reset();
755
756   for (int i = 0; i < DS_SPEAKER_COUNT; i++)
757   {
758     if (speakers & DSChannelOrder[i])
759       m_channelLayout += AEChannelNames[i];
760   }
761 }
762
763 DWORD CAESinkDirectSound::SpeakerMaskFromAEChannels(const CAEChannelInfo &channels)
764 {
765   DWORD mask = 0;
766
767   for (unsigned int i = 0; i < channels.Count(); i++)
768   {
769     for (unsigned int j = 0; j < DS_SPEAKER_COUNT; j++)
770       if (channels[i] == AEChannelNames[j])
771         mask |= DSChannelOrder[j];
772   }
773
774   return mask;
775 }
776
777 const char *CAESinkDirectSound::dserr2str(int err)
778 {
779   switch (err)
780   {
781     case DS_OK: return "DS_OK";
782     case DS_NO_VIRTUALIZATION: return "DS_NO_VIRTUALIZATION";
783     case DSERR_ALLOCATED: return "DS_NO_VIRTUALIZATION";
784     case DSERR_CONTROLUNAVAIL: return "DSERR_CONTROLUNAVAIL";
785     case DSERR_INVALIDPARAM: return "DSERR_INVALIDPARAM";
786     case DSERR_INVALIDCALL: return "DSERR_INVALIDCALL";
787     case DSERR_GENERIC: return "DSERR_GENERIC";
788     case DSERR_PRIOLEVELNEEDED: return "DSERR_PRIOLEVELNEEDED";
789     case DSERR_OUTOFMEMORY: return "DSERR_OUTOFMEMORY";
790     case DSERR_BADFORMAT: return "DSERR_BADFORMAT";
791     case DSERR_UNSUPPORTED: return "DSERR_UNSUPPORTED";
792     case DSERR_NODRIVER: return "DSERR_NODRIVER";
793     case DSERR_ALREADYINITIALIZED: return "DSERR_ALREADYINITIALIZED";
794     case DSERR_NOAGGREGATION: return "DSERR_NOAGGREGATION";
795     case DSERR_BUFFERLOST: return "DSERR_BUFFERLOST";
796     case DSERR_OTHERAPPHASPRIO: return "DSERR_OTHERAPPHASPRIO";
797     case DSERR_UNINITIALIZED: return "DSERR_UNINITIALIZED";
798     case DSERR_NOINTERFACE: return "DSERR_NOINTERFACE";
799     case DSERR_ACCESSDENIED: return "DSERR_ACCESSDENIED";
800     default: return "unknown";
801   }
802 }
803
804 const char *CAESinkDirectSound::WASAPIErrToStr(HRESULT err)
805 {
806   switch(err)
807   {
808     ERRTOSTR(AUDCLNT_E_NOT_INITIALIZED);
809     ERRTOSTR(AUDCLNT_E_ALREADY_INITIALIZED);
810     ERRTOSTR(AUDCLNT_E_WRONG_ENDPOINT_TYPE);
811     ERRTOSTR(AUDCLNT_E_DEVICE_INVALIDATED);
812     ERRTOSTR(AUDCLNT_E_NOT_STOPPED);
813     ERRTOSTR(AUDCLNT_E_BUFFER_TOO_LARGE);
814     ERRTOSTR(AUDCLNT_E_OUT_OF_ORDER);
815     ERRTOSTR(AUDCLNT_E_UNSUPPORTED_FORMAT);
816     ERRTOSTR(AUDCLNT_E_INVALID_SIZE);
817     ERRTOSTR(AUDCLNT_E_DEVICE_IN_USE);
818     ERRTOSTR(AUDCLNT_E_BUFFER_OPERATION_PENDING);
819     ERRTOSTR(AUDCLNT_E_THREAD_NOT_REGISTERED);
820     ERRTOSTR(AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED);
821     ERRTOSTR(AUDCLNT_E_ENDPOINT_CREATE_FAILED);
822     ERRTOSTR(AUDCLNT_E_SERVICE_NOT_RUNNING);
823     ERRTOSTR(AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED);
824     ERRTOSTR(AUDCLNT_E_EXCLUSIVE_MODE_ONLY);
825     ERRTOSTR(AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL);
826     ERRTOSTR(AUDCLNT_E_EVENTHANDLE_NOT_SET);
827     ERRTOSTR(AUDCLNT_E_INCORRECT_BUFFER_SIZE);
828     ERRTOSTR(AUDCLNT_E_BUFFER_SIZE_ERROR);
829     ERRTOSTR(AUDCLNT_E_CPUUSAGE_EXCEEDED);
830     ERRTOSTR(AUDCLNT_E_BUFFER_ERROR);
831     ERRTOSTR(AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED);
832     ERRTOSTR(AUDCLNT_E_INVALID_DEVICE_PERIOD);
833     ERRTOSTR(E_POINTER);
834     ERRTOSTR(E_INVALIDARG);
835     ERRTOSTR(E_OUTOFMEMORY);
836     default: break;
837   }
838   return NULL;
839 }
840
841 std::string CAESinkDirectSound::GetDefaultDevice()
842 {
843   IMMDeviceEnumerator* pEnumerator = NULL;
844   IMMDevice*           pDevice = NULL;
845   IPropertyStore*      pProperty = NULL;
846   HRESULT              hr;
847   PROPVARIANT          varName;
848   std::string          strDevName = "default";
849
850   hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
851   if (FAILED(hr))
852   {
853     CLog::Log(LOGERROR, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %s", WASAPIErrToStr(hr));
854     goto failed;
855   }
856
857   hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDevice);
858   if (FAILED(hr))
859   {
860     CLog::Log(LOGERROR, __FUNCTION__": Retrieval of audio endpoint enumeration failed.");
861     goto failed;
862   }
863
864   hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty);
865   if (FAILED(hr))
866   {
867     CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint properties failed.");
868     goto failed;
869   }
870
871   PropVariantInit(&varName);
872   hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName);
873   if (FAILED(hr))
874   {
875     CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint form factor failed.");
876     goto failed;
877   }
878   AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType;
879   PropVariantClear(&varName);
880
881   hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
882   if (FAILED(hr))
883   {
884     CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint GUID failed.");    
885     goto failed;
886   }
887
888   strDevName = localWideToUtf(varName.pwszVal);
889   PropVariantClear(&varName);
890
891 failed:
892
893   SAFE_RELEASE(pProperty);
894   SAFE_RELEASE(pDevice);
895   SAFE_RELEASE(pEnumerator);
896
897   return strDevName;
898 }