13#define LOC QString("AODX: ")
16DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0,
17 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
19#ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
20#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
23#ifndef WAVE_FORMAT_IEEE_FLOAT
24#define WAVE_FORMAT_IEEE_FLOAT 0x0003
27#ifndef WAVE_FORMAT_EXTENSIBLE
28#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
31#ifndef _WAVEFORMATEXTENSIBLE_
46 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
48 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
50 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
72 void FillBuffer(
unsigned char *buffer,
int size);
104 m_UseSPDIF(settings.m_usePassthru)
127using LPFNDSC = HRESULT (WINAPI *) (LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
128using LPFNDSE = HRESULT (WINAPI *) (LPDSENUMCALLBACK, LPVOID);
133 [[maybe_unused]] LPCWSTR lpcstrModule,
136 QString enum_desc = QString::fromWCharArray( lpcstrDesc );
140 [[maybe_unused]] LPCSTR lpcstrModule,
143 QString enum_desc = QString::fromLocal8Bit( lpcstrDesc );
151 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Device %1:" + enum_desc).arg(device_count));
153 if ((device_num == device_count ||
154 (device_num == 0 && !cfg_desc.isEmpty() &&
155 enum_desc.startsWith(cfg_desc, Qt::CaseInsensitive))) && lpGuid)
194 LPFNDSE OurDirectSoundEnumerate;
202 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot open DSOUND.DLL");
212 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Looking for device num:%1 or name:%2")
215 OurDirectSoundEnumerate =
218 if(OurDirectSoundEnumerate)
220 LOG(VB_GENERAL, LOG_ERR,
LOC +
"DirectSoundEnumerate FAILED");
230 OurDirectSoundCreate =
233 if (OurDirectSoundCreate ==
nullptr)
235 LOG(VB_GENERAL, LOG_ERR,
LOC +
"GetProcAddress FAILED");
241 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot create a direct sound device");
257 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot set DS cooperative level");
259 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Initialised DirectSound");
278 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Destroying DirectSound buffer");
289 void *p_write_position, *p_wrap_around;
290 DWORD l_bytes1, l_bytes2, play_pos, write_pos;
299 dsresult = IDirectSoundBuffer_GetCurrentPosition(
m_dsbuffer,
300 &play_pos, &write_pos);
301 if (dsresult != DS_OK)
303 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot get current buffer position");
307 LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO,
LOC + QString(
"play: %1 write: %2 wcursor: %3")
315 LOG(VB_GENERAL, LOG_ERR,
LOC +
"buffer underrun");
321 if ((m_writeCursor < play_pos && m_writeCursor + size >= play_pos) ||
325 std::this_thread::sleep_for(50ms);
332 dsresult = IDirectSoundBuffer_Lock(
342 if (dsresult == DSERR_BUFFERLOST)
346 &p_write_position, &l_bytes1,
347 &p_wrap_around, &l_bytes2, 0);
350 if (dsresult != DS_OK)
352 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot lock buffer, audio dropped");
356 memcpy(p_write_position, buffer, l_bytes1);
358 memcpy(p_wrap_around, buffer + l_bytes1, l_bytes2);
365 IDirectSoundBuffer_Unlock(
m_dsbuffer, p_write_position, l_bytes1,
366 p_wrap_around, l_bytes2);
373 dsresult = IDirectSoundBuffer_Play(
m_dsbuffer, 0, 0, DSBPLAY_LOOPING);
374 if (dsresult == DSERR_BUFFERLOST)
377 dsresult = IDirectSoundBuffer_Play(
m_dsbuffer, 0, 0, DSBPLAY_LOOPING);
379 if (dsresult != DS_OK)
381 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot start playing buffer");
394 devcaps.dwSize =
sizeof(DSCAPS);
396 m_priv->InitDirectSound(passthrough);
397 if ((!m_priv->m_dsobject || !m_priv->m_dsound_dll) ||
398 FAILED(IDirectSound_GetCaps(m_priv->m_dsobject, &devcaps)) )
404 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"GetCaps sample rate min: %1 max: %2")
405 .arg(devcaps.dwMinSecondarySampleRate)
406 .arg(devcaps.dwMaxSecondarySampleRate));
410 while (DWORD rate = (DWORD)settings->
GetNextRate())
411 if((rate >= devcaps.dwMinSecondarySampleRate) ||
412 (rate <= devcaps.dwMaxSecondarySampleRate))
416 if (devcaps.dwFlags & DSCAPS_PRIMARY8BIT)
418 if (devcaps.dwFlags & DSCAPS_PRIMARY16BIT)
432 for (
uint i = 2; i < 7; i++)
443 DSBUFFERDESC dsbdesc;
447 m_UseSPDIF = m_passthru || m_enc;
448 m_priv->InitDirectSound(m_UseSPDIF);
449 if (!m_priv->m_dsobject || !m_priv->m_dsound_dll)
451 QString message {QCoreApplication::translate(
"AudioOutputDX",
"DirectSound initialization failed")};
452 dispatchError(message);
453 LOG(VB_GENERAL, LOG_ERR, message);
458 m_fragmentSize = 50 * m_outputBytesPerFrame * m_sampleRate / 1000;
460 m_soundcardBufferSize = m_fragmentSize << 2;
462 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"DirectSound buffer size: %1").arg(m_soundcardBufferSize));
464 wf.
Format.nChannels = m_channels;
465 wf.
Format.nSamplesPerSec = m_sampleRate;
466 wf.
Format.nBlockAlign = m_outputBytesPerFrame;
467 wf.
Format.nAvgBytesPerSec = m_sampleRate * m_outputBytesPerFrame;
468 wf.
Format.wBitsPerSample = (m_outputBytesPerFrame << 3) / m_channels;
475 wf.
SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
480 wf.
SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
484 wf.
Format.wFormatTag = WAVE_FORMAT_PCM;
485 wf.
SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
488 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"New format: %1bits %2ch %3Hz %4")
490 .arg(m_sampleRate).arg(m_UseSPDIF ?
"data" :
"PCM"));
501 memset(&dsbdesc, 0,
sizeof(DSBUFFERDESC));
502 dsbdesc.dwSize =
sizeof(DSBUFFERDESC);
503 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2
504 | DSBCAPS_GLOBALFOCUS
505 | DSBCAPS_LOCHARDWARE;
508 dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME;
510 dsbdesc.dwBufferBytes = m_soundcardBufferSize;
511 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wf;
513 if (
FAILED(IDirectSound_CreateSoundBuffer(m_priv->m_dsobject, &dsbdesc,
514 &m_priv->m_dsbuffer,
nullptr)))
518 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
520 IDirectSound_CreateSoundBuffer(m_priv->m_dsobject, &dsbdesc,
521 &m_priv->m_dsbuffer,
nullptr);
524 if (dsresult == DSERR_UNSUPPORTED)
527 QCoreApplication::translate(
"AudioOutputDX",
"Unsupported format for device %1:%2")
528 .arg(m_priv->m_device_num).arg(m_priv->m_device_name)};
529 dispatchError(message);
530 LOG(VB_GENERAL, LOG_ERR, message);
534 QString message {QCoreApplication::translate(
"AudioOutputDX",
"Failed to create DS buffer 0x%1")
535 .arg((DWORD)dsresult, 0, 16)};
536 dispatchError(message);
537 LOG(VB_GENERAL, LOG_ERR, message);
541 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Using software mixer");
543 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Created DirectSound buffer");
550 if (m_priv->m_dsbuffer)
551 m_priv->DestroyDSBuffer();
559 m_priv->FillBuffer(buffer, size);
560 if (!m_priv->m_playStarted)
561 m_priv->StartPlayback();
566 if (!m_priv->m_playStarted)
570 DWORD play_pos, write_pos;
573 dsresult = IDirectSoundBuffer_GetCurrentPosition(m_priv->m_dsbuffer,
574 &play_pos, &write_pos);
575 if (dsresult != DS_OK)
577 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot get current buffer position");
581 buffered = (int)m_priv->m_writeCursor - (
int)play_pos;
584 buffered += m_soundcardBufferSize;
598 dsresult = IDirectSoundBuffer_GetVolume(m_priv->m_dsbuffer, &dxVolume);
599 volume = (int)(pow(10,(
float)dxVolume/20)*100);
601 if (dsresult != DS_OK)
603 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to get volume %1").arg(dxVolume));
607 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Got volume %1").arg(volume));
614 long dxVolume { DSBVOLUME_MIN };
617 float dbAtten = 20 * log10((
float)volume/100.F);
618 dxVolume = (
long)(100.0F * dbAtten);
625 dsresult = IDirectSoundBuffer_SetVolume(m_priv->m_dsbuffer, dxVolume);
627 if (dsresult != DS_OK)
629 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to set volume %1").arg(dxVolume));
633 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Set volume %1").arg(dxVolume));
640 QMap<int, QString> *dxdevs =
new QMap<int, QString>(tmp_priv->
m_device_list);
#define WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_EXTENSIBLE
#define WAVE_FORMAT_DOLBY_AC3_SPDIF
HRESULT(WINAPI *)(LPDSENUMCALLBACK, LPVOID) LPFNDSE
DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16)
HRESULT(WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN) LPFNDSC
void KillAudio(void)
Kill the output thread and cleanup.
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
long m_soundcardBufferSize
void InitSettings(const AudioSettings &settings)
AudioOutputDXPrivate(AudioOutputDX *in_parent)
int InitDirectSound(bool passthrough=false)
QMap< int, QString > m_device_list
LPDIRECTSOUNDBUFFER m_dsbuffer
void DestroyDSBuffer(void)
static int CALLBACK DSEnumCallback(LPGUID lpGuid, LPCSTR lpcstrDesc, LPCSTR lpcstrModule, LPVOID lpContext)
void FillBuffer(unsigned char *buffer, int size)
void ResetDirectSound(void)
void WriteAudio(unsigned char *buffer, int size) override
int GetVolumeChannel(int channel) const override
void SetVolumeChannel(int channel, int volume) override
AudioOutputDXPrivate * m_priv
AudioOutputDX(const AudioSettings &settings)
bool OpenDevice(void) override
AudioOutputSettings * GetOutputSettings(bool passthrough) override
static QMap< int, QString > * GetDXDevices(void)
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
void CloseDevice(void) override
void AddSupportedRate(int rate)
void setPassthrough(int val)
static int FormatToBits(AudioFormat format)
void AddSupportedChannels(int channels)
void AddSupportedFormat(AudioFormat format)
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
union WAVEFORMATEXTENSIBLE::@9 Samples