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