2 * Copyright (C) 2010-2013 Team XBMC
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)
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.
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/>.
23 #include "AESinkDirectSound.h"
24 #include "utils/Log.h"
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>
35 #include <mmdeviceapi.h>
36 #include <Functiondiscoverykeys_devpkey.h>
38 #include "cores/AudioEngine/Utils/AEUtil.h"
39 #include "utils/StringUtils.h"
40 #pragma comment(lib, "Rpcrt4.lib")
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 );
47 #define EXIT_ON_FAILURE(hr, reason, ...) if(FAILED(hr)) {CLog::Log(LOGERROR, reason " - %s", __VA_ARGS__, WASAPIErrToStr(hr)); goto failed;}
49 #define ERRTOSTR(err) case err: return #err
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};
55 static enum AEChannel layoutsByChCount[9][9] = {
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}};
72 struct winEndpointsToAEDeviceType
74 std::string winEndpointType;
75 AEDeviceType aeDeviceType;
78 static const winEndpointsToAEDeviceType winEndpoints[EndpointFormFactor_enum_count] =
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},
93 // implemented in AESinkWASAPI.cpp
94 extern CStdStringA localWideToUtf(LPCWSTR wstr);
96 static BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
99 std::list<DSDevice> &enumerator = *static_cast<std::list<DSDevice>*>(lpContext);
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 )
107 dev.name = localWideToUtf(strW);
112 enumerator.push_back(dev);
117 CAESinkDirectSound::CAESinkDirectSound() :
120 m_encodedFormat (AE_FMT_INVALID),
121 m_AvgBytesPerSec(0 ),
127 m_LastCacheCheck(0 ),
128 m_BufferTimeouts(0 ),
130 m_initialized (false),
133 m_channelLayout.Reset();
136 CAESinkDirectSound::~CAESinkDirectSound()
141 bool CAESinkDirectSound::Initialize(AEAudioFormat &format, std::string &device)
146 LPGUID deviceGUID = NULL;
147 RPC_CSTR wszUuid = NULL;
149 std::string strDeviceGUID = device;
150 std::list<DSDevice> DSDeviceList;
151 std::string deviceFriendlyName;
152 DirectSoundEnumerate(DSEnumCallback, &DSDeviceList);
154 if(StringUtils::EndsWithNoCase(device, std::string("default")))
155 strDeviceGUID = GetDefaultDevice();
157 for (std::list<DSDevice>::iterator itt = DSDeviceList.begin(); itt != DSDeviceList.end(); ++itt)
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)
166 deviceGUID = (*itt).lpGuid;
167 deviceFriendlyName = (*itt).name.c_str();
171 if (hr == RPC_S_OK) RpcStringFree(&wszUuid);
174 hr = DirectSoundCreate(deviceGUID, &m_pDSound, NULL);
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);
182 CLog::Log(LOGERROR, __FUNCTION__": Failed to create the default DirectSound device with error %s.", dserr2str(hr));
189 /* Dodge the null handle on first init by using desktop handle */
191 tmp_hWnd = GetDesktopWindow();
195 CLog::Log(LOGDEBUG, __FUNCTION__": Using Window handle: %d", tmp_hWnd);
197 hr = m_pDSound->SetCooperativeLevel(tmp_hWnd, DSSCL_PRIORITY);
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();
207 WAVEFORMATEXTENSIBLE wfxex = {0};
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))
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;
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;
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;
234 m_AvgBytesPerSec = wfxex.Format.nAvgBytesPerSec;
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
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 */
248 dsbdesc.dwBufferBytes = m_dwBufferLen;
249 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wfxex;
251 // now create the stream buffer
252 HRESULT res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
255 if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE)
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);
265 SAFE_RELEASE(m_pBuffer);
266 CLog::Log(LOGERROR, __FUNCTION__": cannot create secondary buffer (%s)", dserr2str(res));
270 CLog::Log(LOGDEBUG, __FUNCTION__": secondary buffer created");
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;
287 m_LastCacheCheck = XbmcThreads::SystemClockMillis();
288 m_initialized = true;
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);
311 void CAESinkDirectSound::Deinitialize()
316 CLog::Log(LOGDEBUG, __FUNCTION__": Cleaning up");
321 SAFE_RELEASE(m_pBuffer);
326 m_pDSound->Release();
329 m_initialized = false;
338 unsigned int CAESinkDirectSound::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
343 DWORD total = m_dwFrameSize * frames;
345 unsigned char* pBuffer = (unsigned char*)data;
347 DWORD bufferStatus = 0;
348 m_pBuffer->GetStatus(&bufferStatus);
349 if (bufferStatus & DSBSTATUS_BUFFERLOST)
351 CLog::Log(LOGDEBUG, __FUNCTION__ ": Buffer allocation was lost. Restoring buffer.");
352 m_pBuffer->Restore();
355 while (GetSpace() < total)
362 Sleep(total * 1000 / m_AvgBytesPerSec);
370 LPVOID start = NULL, startWrap = NULL;
371 DWORD size = 0, sizeWrap = 0;
372 if (m_BufferOffset >= m_dwBufferLen) // Wrap-around manually
374 DWORD dwWriteBytes = std::min((int)m_dwChunkSize, (int)len);
375 HRESULT res = m_pBuffer->Lock(m_BufferOffset, dwWriteBytes, &start, &size, &startWrap, &sizeWrap, 0);
378 CLog::Log(LOGERROR, __FUNCTION__ ": Unable to lock buffer at offset %u. HRESULT: 0x%08x", m_BufferOffset, res);
383 memcpy(start, pBuffer, size);
388 m_BufferOffset += size;
389 if (startWrap) // Write-region wraps to beginning of buffer
391 memcpy(startWrap, pBuffer, sizeWrap);
392 m_BufferOffset = sizeWrap;
398 m_CacheLen += size + sizeWrap; // This data is now in the cache
399 m_pBuffer->Unlock(start, size, startWrap, sizeWrap);
404 return (total - len) / m_dwFrameSize; // Frames used
407 void CAESinkDirectSound::Stop()
413 void CAESinkDirectSound::Drain()
415 if (!m_initialized || m_isDirtyDS)
419 HRESULT res = m_pBuffer->SetCurrentPosition(0);
422 CLog::Log(LOGERROR,__FUNCTION__ ": SetCurrentPosition failed. Unable to determine buffer status. HRESULT = 0x%08x", res);
430 double CAESinkDirectSound::GetDelay()
435 /* Make sure we know how much data is in the cache */
436 if (!UpdateCacheStatus())
439 /** returns current cached data duration in seconds */
440 double delay = (double)m_CacheLen / (double)m_AvgBytesPerSec;
444 double CAESinkDirectSound::GetCacheTotal()
446 /** returns total cache capacity in seconds */
447 return (double)m_dwBufferLen / (double)m_AvgBytesPerSec;
450 void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force)
452 CAEDeviceInfo deviceInfo;
454 IMMDeviceEnumerator* pEnumerator = NULL;
455 IMMDeviceCollection* pEnumDevices = NULL;
459 std::string strDD = GetDefaultDevice();
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)
467 hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices);
468 EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.")
470 hr = pEnumDevices->GetCount(&uiCount);
471 EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.")
473 for (UINT i = 0; i < uiCount; i++)
475 IMMDevice *pDevice = NULL;
476 IPropertyStore *pProperty = NULL;
478 PropVariantInit(&varName);
480 deviceInfo.m_channels.Reset();
481 deviceInfo.m_dataFormats.clear();
482 deviceInfo.m_sampleRates.clear();
484 hr = pEnumDevices->Item(i, &pDevice);
487 CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint failed.");
491 hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty);
494 CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint properties failed.");
495 SAFE_RELEASE(pDevice);
499 hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName);
502 CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint device name failed.");
503 SAFE_RELEASE(pDevice);
504 SAFE_RELEASE(pProperty);
508 std::string strFriendlyName = localWideToUtf(varName.pwszVal);
509 PropVariantClear(&varName);
511 hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
514 CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint GUID failed.");
515 SAFE_RELEASE(pDevice);
516 SAFE_RELEASE(pProperty);
520 std::string strDevName = localWideToUtf(varName.pwszVal);
521 PropVariantClear(&varName);
523 hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName);
526 CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint form factor failed.");
527 SAFE_RELEASE(pDevice);
528 SAFE_RELEASE(pProperty);
531 std::string strWinDevType = winEndpoints[(EndpointFormFactor)varName.uiVal].winEndpointType;
532 AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType;
534 PropVariantClear(&varName);
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);
541 CLog::Log(LOGERROR, __FUNCTION__": Activate device failed (%s)", WASAPIErrToStr(hr));
545 //hr = pClient->GetMixFormat(&pwfxex);
546 hr = pProperty->GetValue(PKEY_AudioEngine_DeviceFormat, &varName);
547 if (SUCCEEDED(hr) && varName.blob.cbSize > 0)
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));
557 CLog::Log(LOGERROR, __FUNCTION__": Getting DeviceFormat failed (%s)", WASAPIErrToStr(hr));
561 SAFE_RELEASE(pDevice);
562 SAFE_RELEASE(pProperty);
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;
569 deviceInfoList.push_back(deviceInfo);
571 // add the default device with m_deviceName = default
572 if(strDD == strDevName)
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);
586 CLog::Log(LOGERROR, __FUNCTION__": Failed to enumerate WASAPI endpoint devices (%s).", WASAPIErrToStr(hr));
588 SAFE_RELEASE(pEnumDevices);
589 SAFE_RELEASE(pEnumerator);
593 ///////////////////////////////////////////////////////////////////////////////
595 void CAESinkDirectSound::CheckPlayStatus()
598 m_pBuffer->GetStatus(&status);
600 if (!(status & DSBSTATUS_PLAYING) && m_CacheLen != 0) // If we have some data, see if we can start playback
602 HRESULT hr = m_pBuffer->Play(0, 0, DSBPLAY_LOOPING);
603 CLog::Log(LOGDEBUG,__FUNCTION__ ": Resuming Playback");
605 CLog::Log(LOGERROR, __FUNCTION__": Failed to play the DirectSound buffer: %s", dserr2str(hr));
609 bool CAESinkDirectSound::UpdateCacheStatus()
611 CSingleLock lock (m_runLock);
613 DWORD playCursor = 0, writeCursor = 0;
614 HRESULT res = m_pBuffer->GetCurrentPosition(&playCursor, &writeCursor); // Get the current playback and safe write positions
617 CLog::Log(LOGERROR,__FUNCTION__ ": GetCurrentPosition failed. Unable to determine buffer status. HRESULT = 0x%08x", res);
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)
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)
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)
647 if (m_BufferTimeouts > 10)
654 m_BufferTimeouts = 0;
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
659 else if (m_BufferOffset > playCursor)
660 m_CacheLen = m_BufferOffset - playCursor;
662 m_CacheLen = m_dwBufferLen - (playCursor - m_BufferOffset);
667 unsigned int CAESinkDirectSound::GetSpace()
669 CSingleLock lock (m_runLock);
670 if (!UpdateCacheStatus())
672 unsigned int space = m_dwBufferLen - m_CacheLen;
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;
683 void CAESinkDirectSound::AEChannelsFromSpeakerMask(DWORD speakers)
685 m_channelLayout.Reset();
687 for (int i = 0; i < DS_SPEAKER_COUNT; i++)
689 if (speakers & DSChannelOrder[i])
690 m_channelLayout += AEChannelNames[i];
694 DWORD CAESinkDirectSound::SpeakerMaskFromAEChannels(const CAEChannelInfo &channels)
698 for (unsigned int i = 0; i < channels.Count(); i++)
700 for (unsigned int j = 0; j < DS_SPEAKER_COUNT; j++)
701 if (channels[i] == AEChannelNames[j])
702 mask |= DSChannelOrder[j];
708 const char *CAESinkDirectSound::dserr2str(int err)
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";
741 const char *CAESinkDirectSound::WASAPIErrToStr(HRESULT err)
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);
771 ERRTOSTR(E_INVALIDARG);
772 ERRTOSTR(E_OUTOFMEMORY);
778 std::string CAESinkDirectSound::GetDefaultDevice()
780 IMMDeviceEnumerator* pEnumerator = NULL;
781 IMMDevice* pDevice = NULL;
782 IPropertyStore* pProperty = NULL;
785 std::string strDevName = "default";
787 hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
790 CLog::Log(LOGERROR, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %s", WASAPIErrToStr(hr));
794 hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDevice);
797 CLog::Log(LOGERROR, __FUNCTION__": Retrieval of audio endpoint enumeration failed.");
801 hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty);
804 CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint properties failed.");
808 PropVariantInit(&varName);
809 hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName);
812 CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint form factor failed.");
815 AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType;
816 PropVariantClear(&varName);
818 hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName);
821 CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint GUID failed.");
825 strDevName = localWideToUtf(varName.pwszVal);
826 PropVariantClear(&varName);
830 SAFE_RELEASE(pProperty);
831 SAFE_RELEASE(pDevice);
832 SAFE_RELEASE(pEnumerator);