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