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