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 Error(QObject::tr(
"DirectSound initialization failed"));
455 m_fragmentSize = 50 * m_outputBytesPerFrame * m_sampleRate / 1000;
457 m_soundcardBufferSize = m_fragmentSize << 2;
459 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"DirectSound buffer size: %1").arg(m_soundcardBufferSize));
461 wf.
Format.nChannels = m_channels;
462 wf.
Format.nSamplesPerSec = m_sampleRate;
463 wf.
Format.nBlockAlign = m_outputBytesPerFrame;
464 wf.
Format.nAvgBytesPerSec = m_sampleRate * m_outputBytesPerFrame;
465 wf.
Format.wBitsPerSample = (m_outputBytesPerFrame << 3) / m_channels;
472 wf.
SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
477 wf.
SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
481 wf.
Format.wFormatTag = WAVE_FORMAT_PCM;
482 wf.
SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
485 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"New format: %1bits %2ch %3Hz %4")
487 .arg(m_sampleRate).arg(m_UseSPDIF ?
"data" :
"PCM"));
498 memset(&dsbdesc, 0,
sizeof(DSBUFFERDESC));
499 dsbdesc.dwSize =
sizeof(DSBUFFERDESC);
500 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2
501 | DSBCAPS_GLOBALFOCUS
502 | DSBCAPS_LOCHARDWARE;
505 dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME;
507 dsbdesc.dwBufferBytes = m_soundcardBufferSize;
508 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wf;
510 if (
FAILED(IDirectSound_CreateSoundBuffer(m_priv->m_dsobject, &dsbdesc,
511 &m_priv->m_dsbuffer,
nullptr)))
515 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
517 IDirectSound_CreateSoundBuffer(m_priv->m_dsobject, &dsbdesc,
518 &m_priv->m_dsbuffer,
nullptr);
521 if (dsresult == DSERR_UNSUPPORTED)
522 Error(QObject::tr(
"Unsupported format for device %1:%2")
523 .arg(m_priv->m_device_num).arg(m_priv->m_device_name));
525 Error(QObject::tr(
"Failed to create DS buffer 0x%1")
526 .arg((DWORD)dsresult, 0, 16));
529 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Using software mixer");
531 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Created DirectSound buffer");
538 if (m_priv->m_dsbuffer)
539 m_priv->DestroyDSBuffer();
547 m_priv->FillBuffer(buffer, size);
548 if (!m_priv->m_playStarted)
549 m_priv->StartPlayback();
554 if (!m_priv->m_playStarted)
558 DWORD play_pos, write_pos;
561 dsresult = IDirectSoundBuffer_GetCurrentPosition(m_priv->m_dsbuffer,
562 &play_pos, &write_pos);
563 if (dsresult != DS_OK)
565 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Cannot get current buffer position");
569 buffered = (int)m_priv->m_writeCursor - (
int)play_pos;
572 buffered += m_soundcardBufferSize;
586 dsresult = IDirectSoundBuffer_GetVolume(m_priv->m_dsbuffer, &dxVolume);
587 volume = (int)(pow(10,(
float)dxVolume/20)*100);
589 if (dsresult != DS_OK)
591 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to get volume %1").arg(dxVolume));
595 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Got volume %1").arg(volume));
602 long dxVolume { DSBVOLUME_MIN };
605 float dbAtten = 20 * log10((
float)volume/100.F);
606 dxVolume = (
long)(100.0F * dbAtten);
613 dsresult = IDirectSoundBuffer_SetVolume(m_priv->m_dsbuffer, dxVolume);
615 if (dsresult != DS_OK)
617 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to set volume %1").arg(dxVolume));
621 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Set volume %1").arg(dxVolume));
628 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::@2 Samples