12#define LOC QString("AODX: ")
15DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0,
16 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
18#ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
19#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
22#ifndef WAVE_FORMAT_IEEE_FLOAT
23#define WAVE_FORMAT_IEEE_FLOAT 0x0003
26#ifndef WAVE_FORMAT_EXTENSIBLE
27#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
30#ifndef _WAVEFORMATEXTENSIBLE_
45 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
47 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
49 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
71 void FillBuffer(
unsigned char *buffer,
int size);
103 m_UseSPDIF(settings.m_usePassthru)
126using LPFNDSC = HRESULT (WINAPI *) (LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
127using LPFNDSE = HRESULT (WINAPI *) (LPDSENUMCALLBACK, LPVOID);
132 [[maybe_unused]] LPCWSTR lpcstrModule,
135 QString enum_desc = QString::fromWCharArray( lpcstrDesc );
139 [[maybe_unused]] LPCSTR lpcstrModule,
142 QString enum_desc = QString::fromLocal8Bit( lpcstrDesc );
150 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Device %1:" + enum_desc).arg(device_count));
152 if ((device_num == device_count ||
153 (device_num == 0 && !cfg_desc.isEmpty() &&
154 enum_desc.startsWith(cfg_desc, Qt::CaseInsensitive))) && lpGuid)
193 LPFNDSE OurDirectSoundEnumerate;
201 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot open DSOUND.DLL");
211 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Looking for device num:%1 or name:%2")
214 OurDirectSoundEnumerate =
217 if(OurDirectSoundEnumerate)
219 LOG(VB_GENERAL, LOG_ERR,
LOC +
"DirectSoundEnumerate FAILED");
229 OurDirectSoundCreate =
232 if (OurDirectSoundCreate ==
nullptr)
234 LOG(VB_GENERAL, LOG_ERR,
LOC +
"GetProcAddress FAILED");
240 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot create a direct sound device");
256 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot set DS cooperative level");
258 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Initialised DirectSound");
277 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Destroying DirectSound buffer");
288 void *p_write_position, *p_wrap_around;
289 DWORD l_bytes1, l_bytes2, play_pos, write_pos;
298 dsresult = IDirectSoundBuffer_GetCurrentPosition(
m_dsbuffer,
299 &play_pos, &write_pos);
300 if (dsresult != DS_OK)
302 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot get current buffer position");
306 LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO,
LOC + QString(
"play: %1 write: %2 wcursor: %3")
314 LOG(VB_GENERAL, LOG_ERR,
LOC +
"buffer underrun");
320 if ((m_writeCursor < play_pos && m_writeCursor + size >= play_pos) ||
331 dsresult = IDirectSoundBuffer_Lock(
341 if (dsresult == DSERR_BUFFERLOST)
345 &p_write_position, &l_bytes1,
346 &p_wrap_around, &l_bytes2, 0);
349 if (dsresult != DS_OK)
351 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot lock buffer, audio dropped");
355 memcpy(p_write_position, buffer, l_bytes1);
357 memcpy(p_wrap_around, buffer + l_bytes1, l_bytes2);
364 IDirectSoundBuffer_Unlock(
m_dsbuffer, p_write_position, l_bytes1,
365 p_wrap_around, l_bytes2);
372 dsresult = IDirectSoundBuffer_Play(
m_dsbuffer, 0, 0, DSBPLAY_LOOPING);
373 if (dsresult == DSERR_BUFFERLOST)
376 dsresult = IDirectSoundBuffer_Play(
m_dsbuffer, 0, 0, DSBPLAY_LOOPING);
378 if (dsresult != DS_OK)
380 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot start playing buffer");
393 devcaps.dwSize =
sizeof(DSCAPS);
395 m_priv->InitDirectSound(passthrough);
396 if ((!m_priv->m_dsobject || !m_priv->m_dsound_dll) ||
397 FAILED(IDirectSound_GetCaps(m_priv->m_dsobject, &devcaps)) )
403 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"GetCaps sample rate min: %1 max: %2")
404 .arg(devcaps.dwMinSecondarySampleRate)
405 .arg(devcaps.dwMaxSecondarySampleRate));
409 while (DWORD rate = (DWORD)settings->
GetNextRate())
410 if((rate >= devcaps.dwMinSecondarySampleRate) ||
411 (rate <= devcaps.dwMaxSecondarySampleRate))
415 if (devcaps.dwFlags & DSCAPS_PRIMARY8BIT)
417 if (devcaps.dwFlags & DSCAPS_PRIMARY16BIT)
431 for (
uint i = 2; i < 7; i++)
442 DSBUFFERDESC dsbdesc;
446 m_UseSPDIF = m_passthru || m_enc;
447 m_priv->InitDirectSound(m_UseSPDIF);
448 if (!m_priv->m_dsobject || !m_priv->m_dsound_dll)
450 QString message {QCoreApplication::translate(
"AudioOutputDX",
"DirectSound initialization failed")};
451 dispatchError(message);
452 LOG(VB_GENERAL, LOG_ERR, message);
457 m_fragmentSize = 50 * m_outputBytesPerFrame * m_sampleRate / 1000;
459 m_soundcardBufferSize = m_fragmentSize << 2;
461 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"DirectSound buffer size: %1").arg(m_soundcardBufferSize));
463 wf.
Format.nChannels = m_channels;
464 wf.
Format.nSamplesPerSec = m_sampleRate;
465 wf.
Format.nBlockAlign = m_outputBytesPerFrame;
466 wf.
Format.nAvgBytesPerSec = m_sampleRate * m_outputBytesPerFrame;
467 wf.
Format.wBitsPerSample = (m_outputBytesPerFrame << 3) / m_channels;
474 wf.
SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
479 wf.
SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
483 wf.
Format.wFormatTag = WAVE_FORMAT_PCM;
484 wf.
SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
487 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"New format: %1bits %2ch %3Hz %4")
489 .arg(m_sampleRate).arg(m_UseSPDIF ?
"data" :
"PCM"));
500 memset(&dsbdesc, 0,
sizeof(DSBUFFERDESC));
501 dsbdesc.dwSize =
sizeof(DSBUFFERDESC);
502 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2
503 | DSBCAPS_GLOBALFOCUS
504 | DSBCAPS_LOCHARDWARE;
507 dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME;
509 dsbdesc.dwBufferBytes = m_soundcardBufferSize;
510 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wf;
512 if (
FAILED(IDirectSound_CreateSoundBuffer(m_priv->m_dsobject, &dsbdesc,
513 &m_priv->m_dsbuffer,
nullptr)))
517 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
519 IDirectSound_CreateSoundBuffer(m_priv->m_dsobject, &dsbdesc,
520 &m_priv->m_dsbuffer,
nullptr);
523 if (dsresult == DSERR_UNSUPPORTED)
526 QCoreApplication::translate(
"AudioOutputDX",
"Unsupported format for device %1:%2")
527 .arg(m_priv->m_device_num).arg(m_priv->m_device_name)};
528 dispatchError(message);
529 LOG(VB_GENERAL, LOG_ERR, message);
533 QString message {QCoreApplication::translate(
"AudioOutputDX",
"Failed to create DS buffer 0x%1")
534 .arg((DWORD)dsresult, 0, 16)};
535 dispatchError(message);
536 LOG(VB_GENERAL, LOG_ERR, message);
540 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Using software mixer");
542 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Created DirectSound buffer");
549 if (m_priv->m_dsbuffer)
550 m_priv->DestroyDSBuffer();
558 m_priv->FillBuffer(buffer, size);
559 if (!m_priv->m_playStarted)
560 m_priv->StartPlayback();
565 if (!m_priv->m_playStarted)
569 DWORD play_pos, write_pos;
572 dsresult = IDirectSoundBuffer_GetCurrentPosition(m_priv->m_dsbuffer,
573 &play_pos, &write_pos);
574 if (dsresult != DS_OK)
576 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot get current buffer position");
580 buffered = (int)m_priv->m_writeCursor - (
int)play_pos;
583 buffered += m_soundcardBufferSize;
597 dsresult = IDirectSoundBuffer_GetVolume(m_priv->m_dsbuffer, &dxVolume);
598 volume = (int)(pow(10,(
float)dxVolume/20)*100);
600 if (dsresult != DS_OK)
602 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to get volume %1").arg(dxVolume));
606 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Got volume %1").arg(volume));
613 long dxVolume { DSBVOLUME_MIN };
616 float dbAtten = 20 * log10((
float)volume/100.F);
617 dxVolume = (
long)(100.0F * dbAtten);
624 dsresult = IDirectSoundBuffer_SetVolume(m_priv->m_dsbuffer, dxVolume);
626 if (dsresult != DS_OK)
628 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to set volume %1").arg(dxVolume));
632 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Set volume %1").arg(dxVolume));
639 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