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