MythTV master
audiooutputdx.cpp
Go to the documentation of this file.
1#include <iostream>
2#include <cmath>
3#include <thread>
4
6#include "audiooutputdx.h"
7
8#include <windows.h>
9#include <mmsystem.h>
10#include <dsound.h>
11#include <unistd.h>
12
13#define LOC QString("AODX: ")
14
15#include <initguid.h>
16DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0,
17 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
18
19#ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
20#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
21#endif
22
23#ifndef WAVE_FORMAT_IEEE_FLOAT
24#define WAVE_FORMAT_IEEE_FLOAT 0x0003
25#endif
26
27#ifndef WAVE_FORMAT_EXTENSIBLE
28#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
29#endif
30
31#ifndef _WAVEFORMATEXTENSIBLE_
33 WAVEFORMATEX Format;
34 union {
35 WORD wValidBitsPerSample; // bits of precision
36 WORD wSamplesPerBlock; // valid if wBitsPerSample==0
37 WORD wReserved; // If neither applies, set to zero
39 DWORD dwChannelMask; // which channels are present in stream
41};
43#endif
44
45DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT,
46 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
47DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM,
48 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
49DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF,
50 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
51
53{
54 public:
55 explicit AudioOutputDXPrivate(AudioOutputDX *in_parent) :
56 m_parent(in_parent) {}
57
59 {
61
62 if (m_dsobject)
63 IDirectSound_Release(m_dsobject);
64
65 if (m_dsound_dll)
66 FreeLibrary(m_dsound_dll);
67 }
68
69 int InitDirectSound(bool passthrough = false);
70 void ResetDirectSound(void);
71 void DestroyDSBuffer(void);
72 void FillBuffer(unsigned char *buffer, int size);
73 bool StartPlayback(void);
74#ifdef UNICODE
75 static int CALLBACK DSEnumCallback(LPGUID lpGuid,
76 LPCWSTR lpcstrDesc,
77 LPCWSTR lpcstrModule,
78 LPVOID lpContext);
79#else
80 static int CALLBACK DSEnumCallback(LPGUID lpGuid,
81 LPCSTR lpcstrDesc,
82 LPCSTR lpcstrModule,
83 LPVOID lpContext);
84#endif
85 public:
87 HINSTANCE m_dsound_dll {nullptr};
88 LPDIRECTSOUND m_dsobject {nullptr};
89 LPDIRECTSOUNDBUFFER m_dsbuffer {nullptr};
90 bool m_playStarted {false};
91 DWORD m_writeCursor {0};
93 GUID *m_chosenGUID {nullptr};
95 int m_device_num {0};
97 QMap<int, QString> m_device_list;
98};
99
100
102 AudioOutputBase(settings),
103 m_priv(new AudioOutputDXPrivate(this)),
104 m_UseSPDIF(settings.m_usePassthru)
105{
106 timeBeginPeriod(1);
107 InitSettings(settings);
108 if (m_passthruDevice == "auto" || m_passthruDevice.toLower() == "default")
110 else
111 m_discreteDigital = true;
112 if (settings.m_init)
113 Reconfigure(settings);
114}
115
117{
118 KillAudio();
119 if (m_priv)
120 {
121 delete m_priv;
122 m_priv = nullptr;
123 }
124 timeEndPeriod(1);
125}
126
127using LPFNDSC = HRESULT (WINAPI *) (LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
128using LPFNDSE = HRESULT (WINAPI *) (LPDSENUMCALLBACK, LPVOID);
129
130#ifdef UNICODE
131int CALLBACK AudioOutputDXPrivate::DSEnumCallback(LPGUID lpGuid,
132 LPCWSTR lpcstrDesc,
133 [[maybe_unused]] LPCWSTR lpcstrModule,
134 LPVOID lpContext)
135{
136 QString enum_desc = QString::fromWCharArray( lpcstrDesc );
137#else
138int CALLBACK AudioOutputDXPrivate::DSEnumCallback(LPGUID lpGuid,
139 LPCSTR lpcstrDesc,
140 [[maybe_unused]] LPCSTR lpcstrModule,
141 LPVOID lpContext)
142{
143 QString enum_desc = QString::fromLocal8Bit( lpcstrDesc );
144
145#endif
146 AudioOutputDXPrivate *context = static_cast<AudioOutputDXPrivate*>(lpContext);
147 const QString cfg_desc = context->m_device_name;
148 const int device_num = context->m_device_num;
149 const int device_count = context->m_device_count;
150
151 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Device %1:" + enum_desc).arg(device_count));
152
153 if ((device_num == device_count ||
154 (device_num == 0 && !cfg_desc.isEmpty() &&
155 enum_desc.startsWith(cfg_desc, Qt::CaseInsensitive))) && lpGuid)
156 {
157 context->m_deviceGUID = *lpGuid;
158 context->m_chosenGUID =
159 &(context->m_deviceGUID);
160 context->m_device_name = enum_desc;
161 context->m_device_num = device_count;
162 }
163
164 context->m_device_list.insert(device_count, enum_desc);
165 context->m_device_count++;
166 return 1;
167}
168
170{
172
173 if (m_dsobject)
174 {
175 IDirectSound_Release(m_dsobject);
176 m_dsobject = nullptr;
177 }
178
179 if (m_dsound_dll)
180 {
181 FreeLibrary(m_dsound_dll);
182 m_dsound_dll = nullptr;
183 }
184
185 m_chosenGUID = nullptr;
186 m_device_count = 0;
187 m_device_num = 0;
188 m_device_list.clear();
189}
190
192{
193 LPFNDSC OurDirectSoundCreate;
194 LPFNDSE OurDirectSoundEnumerate;
195 bool ok;
196
198
199 m_dsound_dll = LoadLibrary(TEXT("DSOUND.DLL"));
200 if (m_dsound_dll == nullptr)
201 {
202 LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot open DSOUND.DLL");
203 goto error;
204 }
205
206 if (m_parent) // parent can be nullptr only when called from GetDXDevices()
207 m_device_name = passthrough ?
209 m_device_name = m_device_name.section(':', 1);
210 m_device_num = m_device_name.toInt(&ok, 10);
211
212 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Looking for device num:%1 or name:%2")
213 .arg(m_device_num).arg(m_device_name));
214
215 OurDirectSoundEnumerate =
216 (LPFNDSE)GetProcAddress(m_dsound_dll, "DirectSoundEnumerateA");
217
218 if(OurDirectSoundEnumerate)
219 if(FAILED(OurDirectSoundEnumerate(DSEnumCallback, this) != DS_OK))
220 LOG(VB_GENERAL, LOG_ERR, LOC + "DirectSoundEnumerate FAILED");
221
222 if (!m_chosenGUID)
223 {
224 m_device_num = 0;
225 m_device_name = "Primary Sound Driver";
226 }
227
228 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Using device %1:%2").arg(m_device_num).arg(m_device_name));
229
230 OurDirectSoundCreate =
231 (LPFNDSC)GetProcAddress(m_dsound_dll, "DirectSoundCreate");
232
233 if (OurDirectSoundCreate == nullptr)
234 {
235 LOG(VB_GENERAL, LOG_ERR, LOC + "GetProcAddress FAILED");
236 goto error;
237 }
238
239 if (FAILED(OurDirectSoundCreate(m_chosenGUID, &m_dsobject, nullptr)))
240 {
241 LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot create a direct sound device");
242 goto error;
243 }
244
245 /* Set DirectSound Cooperative level, ie what control we want over Windows
246 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
247 * settings of the primary buffer, but also that only the sound of our
248 * application will be hearable when it will have the focus.
249 * !!! (this is not really working as intended yet because to set the
250 * cooperative level you need the window handle of your application, and
251 * I don't know of any easy way to get it. Especially since we might play
252 * sound without any video, and so what window handle should we use ???
253 * The hack for now is to use the Desktop window handle - it seems to be
254 * working */
255 if (FAILED(IDirectSound_SetCooperativeLevel(m_dsobject, GetDesktopWindow(),
256 DSSCL_EXCLUSIVE)))
257 LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot set DS cooperative level");
258
259 LOG(VB_AUDIO, LOG_INFO, LOC + "Initialised DirectSound");
260
261 return 0;
262
263 error:
264 m_dsobject = nullptr;
265 if (m_dsound_dll)
266 {
267 FreeLibrary(m_dsound_dll);
268 m_dsound_dll = nullptr;
269 }
270 return -1;
271}
272
274{
275 if (!m_dsbuffer)
276 return;
277
278 LOG(VB_AUDIO, LOG_INFO, LOC + "Destroying DirectSound buffer");
279 IDirectSoundBuffer_Stop(m_dsbuffer);
280 m_writeCursor = 0;
281 IDirectSoundBuffer_SetCurrentPosition(m_dsbuffer, m_writeCursor);
282 m_playStarted = false;
283 IDirectSoundBuffer_Release(m_dsbuffer);
284 m_dsbuffer = nullptr;
285}
286
287void AudioOutputDXPrivate::FillBuffer(unsigned char *buffer, int size)
288{
289 void *p_write_position, *p_wrap_around;
290 DWORD l_bytes1, l_bytes2, play_pos, write_pos;
291 HRESULT dsresult;
292
293 if (!m_dsbuffer)
294 return;
295
296 while (true)
297 {
298
299 dsresult = IDirectSoundBuffer_GetCurrentPosition(m_dsbuffer,
300 &play_pos, &write_pos);
301 if (dsresult != DS_OK)
302 {
303 LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot get current buffer position");
304 return;
305 }
306
307 LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO, LOC + QString("play: %1 write: %2 wcursor: %3")
308 .arg(play_pos).arg(write_pos).arg(m_writeCursor));
309
310 while ((m_writeCursor < write_pos &&
311 ((m_writeCursor >= play_pos && write_pos >= play_pos) ||
312 (m_writeCursor < play_pos && write_pos < play_pos))) ||
313 (m_writeCursor >= play_pos && write_pos < play_pos))
314 {
315 LOG(VB_GENERAL, LOG_ERR, LOC + "buffer underrun");
316 m_writeCursor += size;
319 }
320
321 if ((m_writeCursor < play_pos && m_writeCursor + size >= play_pos) ||
322 (m_writeCursor >= play_pos &&
323 m_writeCursor + size >= play_pos + m_parent->m_soundcardBufferSize))
324 {
325 std::this_thread::sleep_for(50ms);
326 continue;
327 }
328
329 break;
330 }
331
332 dsresult = IDirectSoundBuffer_Lock(
333 m_dsbuffer, /* DS buffer */
334 m_writeCursor, /* Start offset */
335 size, /* Number of bytes */
336 &p_write_position, /* Address of lock start */
337 &l_bytes1, /* Bytes locked before wrap */
338 &p_wrap_around, /* Buffer address (if wrap around) */
339 &l_bytes2, /* Count of bytes after wrap around */
340 0); /* Flags */
341
342 if (dsresult == DSERR_BUFFERLOST)
343 {
344 IDirectSoundBuffer_Restore(m_dsbuffer);
345 dsresult = IDirectSoundBuffer_Lock(m_dsbuffer, m_writeCursor, size,
346 &p_write_position, &l_bytes1,
347 &p_wrap_around, &l_bytes2, 0);
348 }
349
350 if (dsresult != DS_OK)
351 {
352 LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot lock buffer, audio dropped");
353 return;
354 }
355
356 memcpy(p_write_position, buffer, l_bytes1);
357 if (p_wrap_around)
358 memcpy(p_wrap_around, buffer + l_bytes1, l_bytes2);
359
360 m_writeCursor += l_bytes1 + l_bytes2;
361
364
365 IDirectSoundBuffer_Unlock(m_dsbuffer, p_write_position, l_bytes1,
366 p_wrap_around, l_bytes2);
367}
368
370{
371 HRESULT dsresult;
372
373 dsresult = IDirectSoundBuffer_Play(m_dsbuffer, 0, 0, DSBPLAY_LOOPING);
374 if (dsresult == DSERR_BUFFERLOST)
375 {
376 IDirectSoundBuffer_Restore(m_dsbuffer);
377 dsresult = IDirectSoundBuffer_Play(m_dsbuffer, 0, 0, DSBPLAY_LOOPING);
378 }
379 if (dsresult != DS_OK)
380 {
381 LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot start playing buffer");
382 m_playStarted = false;
383 return false;
384 }
385
386 m_playStarted=true;
387 return true;
388}
389
391{
393 DSCAPS devcaps;
394 devcaps.dwSize = sizeof(DSCAPS);
395
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)) )
399 {
400 delete settings;
401 return nullptr;
402 }
403
404 LOG(VB_AUDIO, LOG_INFO, LOC + QString("GetCaps sample rate min: %1 max: %2")
405 .arg(devcaps.dwMinSecondarySampleRate)
406 .arg(devcaps.dwMaxSecondarySampleRate));
407
408 /* We shouldn't assume we can do everything between min and max but
409 there's no way to test individual rates, so we'll have to */
410 while (DWORD rate = (DWORD)settings->GetNextRate())
411 if((rate >= devcaps.dwMinSecondarySampleRate) ||
412 (rate <= devcaps.dwMaxSecondarySampleRate))
413 settings->AddSupportedRate(rate);
414
415 /* We can only test for 8 and 16 bit support, DS uses float internally */
416 if (devcaps.dwFlags & DSCAPS_PRIMARY8BIT)
417 settings->AddSupportedFormat(FORMAT_U8);
418 if (devcaps.dwFlags & DSCAPS_PRIMARY16BIT)
420#if 0 // 24-bit integer is not supported
422#endif
423#if 0 // 32-bit integer (OGG) is not supported on all platforms.
425#endif
426#if 0 // 32-bit floating point (AC3) is not supported on all platforms.
428#endif
429
430 /* No way to test anything other than mono or stereo, guess that we can do
431 up to 5.1 */
432 for (uint i = 2; i < 7; i++)
433 settings->AddSupportedChannels(i);
434
435 settings->setPassthrough(0); // Maybe passthrough
436
437 return settings;
438}
439
441{
443 DSBUFFERDESC dsbdesc;
444
445 CloseDevice();
446
447 m_UseSPDIF = m_passthru || m_enc;
448 m_priv->InitDirectSound(m_UseSPDIF);
449 if (!m_priv->m_dsobject || !m_priv->m_dsound_dll)
450 {
451 QString message {QCoreApplication::translate("AudioOutputDX", "DirectSound initialization failed")};
452 dispatchError(message);
453 LOG(VB_GENERAL, LOG_ERR, message);
454 return false;
455 }
456
457 // fragments are 50ms worth of samples
458 m_fragmentSize = 50 * m_outputBytesPerFrame * m_sampleRate / 1000;
459 // DirectSound buffer holds 4 fragments = 200ms worth of samples
460 m_soundcardBufferSize = m_fragmentSize << 2;
461
462 LOG(VB_AUDIO, LOG_INFO, LOC + QString("DirectSound buffer size: %1").arg(m_soundcardBufferSize));
463
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;
470 AudioOutputSettings::FormatToBits(m_outputFormat);
471
472 if (m_UseSPDIF)
473 {
474 wf.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
475 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
476 }
477 else if (m_outputFormat == FORMAT_FLT)
478 {
479 wf.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
480 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
481 }
482 else
483 {
484 wf.Format.wFormatTag = WAVE_FORMAT_PCM;
485 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
486 }
487
488 LOG(VB_AUDIO, LOG_INFO, LOC + QString("New format: %1bits %2ch %3Hz %4")
489 .arg(wf.Samples.wValidBitsPerSample).arg(m_channels)
490 .arg(m_sampleRate).arg(m_UseSPDIF ? "data" : "PCM"));
491
492 if (m_channels <= 2)
493 wf.Format.cbSize = 0;
494 else
495 {
496 wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
497 wf.dwChannelMask = 0x003F; // 0x003F = 5.1 channels
498 wf.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
499 }
500
501 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
502 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
503 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 // Better position accuracy
504 | DSBCAPS_GLOBALFOCUS // Allows background playing
505 | DSBCAPS_LOCHARDWARE; // Needed for 5.1 on emu101k
506
507 if (!m_UseSPDIF)
508 dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME; // Allow volume control
509
510 dsbdesc.dwBufferBytes = m_soundcardBufferSize; // buffer size
511 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wf;
512
513 if (FAILED(IDirectSound_CreateSoundBuffer(m_priv->m_dsobject, &dsbdesc,
514 &m_priv->m_dsbuffer, nullptr)))
515 {
516 /* Vista does not support hardware mixing
517 try without DSBCAPS_LOCHARDWARE */
518 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
519 HRESULT dsresult =
520 IDirectSound_CreateSoundBuffer(m_priv->m_dsobject, &dsbdesc,
521 &m_priv->m_dsbuffer, nullptr);
522 if (FAILED(dsresult))
523 {
524 if (dsresult == DSERR_UNSUPPORTED)
525 {
526 QString message {
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);
531 }
532 else
533 {
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);
538 }
539 return false;
540 }
541 LOG(VB_AUDIO, LOG_INFO, LOC + "Using software mixer");
542 }
543 LOG(VB_AUDIO, LOG_INFO, LOC + "Created DirectSound buffer");
544
545 return true;
546}
547
549{
550 if (m_priv->m_dsbuffer)
551 m_priv->DestroyDSBuffer();
552}
553
554void AudioOutputDX::WriteAudio(unsigned char * buffer, int size)
555{
556 if (size == 0)
557 return;
558
559 m_priv->FillBuffer(buffer, size);
560 if (!m_priv->m_playStarted)
561 m_priv->StartPlayback();
562}
563
565{
566 if (!m_priv->m_playStarted)
567 return 0;
568
569 HRESULT dsresult;
570 DWORD play_pos, write_pos;
571 int buffered;
572
573 dsresult = IDirectSoundBuffer_GetCurrentPosition(m_priv->m_dsbuffer,
574 &play_pos, &write_pos);
575 if (dsresult != DS_OK)
576 {
577 LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot get current buffer position");
578 return 0;
579 }
580
581 buffered = (int)m_priv->m_writeCursor - (int)play_pos;
582
583 if (buffered <= 0)
584 buffered += m_soundcardBufferSize;
585
586 return buffered;
587}
588
589int AudioOutputDX::GetVolumeChannel([[maybe_unused]] int channel) const
590{
591 HRESULT dsresult;
592 long dxVolume = 0;
593 int volume;
594
595 if (m_UseSPDIF)
596 return 100;
597
598 dsresult = IDirectSoundBuffer_GetVolume(m_priv->m_dsbuffer, &dxVolume);
599 volume = (int)(pow(10,(float)dxVolume/20)*100);
600
601 if (dsresult != DS_OK)
602 {
603 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to get volume %1").arg(dxVolume));
604 return volume;
605 }
606
607 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Got volume %1").arg(volume));
608 return volume;
609}
610
611void AudioOutputDX::SetVolumeChannel([[maybe_unused]] int channel, int volume)
612{
613 HRESULT dsresult;
614 long dxVolume { DSBVOLUME_MIN };
615 if (volume > 0)
616 {
617 float dbAtten = 20 * log10((float)volume/100.F);
618 dxVolume = (long)(100.0F * dbAtten);
619 }
620
621 if (m_UseSPDIF)
622 return;
623
624 // dxVolume is attenuation in 100ths of a decibel
625 dsresult = IDirectSoundBuffer_SetVolume(m_priv->m_dsbuffer, dxVolume);
626
627 if (dsresult != DS_OK)
628 {
629 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to set volume %1").arg(dxVolume));
630 return;
631 }
632
633 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Set volume %1").arg(dxVolume));
634}
635
636QMap<int, QString> *AudioOutputDX::GetDXDevices(void)
637{
638 AudioOutputDXPrivate *tmp_priv = new AudioOutputDXPrivate(nullptr);
639 tmp_priv->InitDirectSound(false);
640 QMap<int, QString> *dxdevs = new QMap<int, QString>(tmp_priv->m_device_list);
641 delete tmp_priv;
642 return dxdevs;
643}
644
645/* vim: set expandtab tabstop=4 shiftwidth=4: */
#define LOC
#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
@ FORMAT_U8
@ FORMAT_S32
@ FORMAT_S24
@ FORMAT_FLT
@ FORMAT_S16
void KillAudio(void)
Kill the output thread and cleanup.
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
QString m_passthruDevice
void InitSettings(const AudioSettings &settings)
AudioOutputDXPrivate(AudioOutputDX *in_parent)
LPDIRECTSOUND m_dsobject
AudioOutputDX * m_parent
int InitDirectSound(bool passthrough=false)
QMap< int, QString > m_device_list
LPDIRECTSOUNDBUFFER m_dsbuffer
static int CALLBACK DSEnumCallback(LPGUID lpGuid, LPCSTR lpcstrDesc, LPCSTR lpcstrModule, LPVOID lpContext)
void FillBuffer(unsigned char *buffer, int size)
void WriteAudio(unsigned char *buffer, int size) override
int GetVolumeChannel(int channel) const override
void SetVolumeChannel(int channel, int volume) override
AudioOutputDXPrivate * m_priv
Definition: audiooutputdx.h:31
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
virtual ~AudioOutputDX()
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.
Definition: audiosettings.h:85
unsigned int uint
Definition: compat.h:60
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
def error(message)
Definition: smolt.py:409
union WAVEFORMATEXTENSIBLE::@9 Samples