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 Error(QObject::tr("DirectSound initialization failed"));
451 return false;
452 }
453
454 // fragments are 50ms worth of samples
455 m_fragmentSize = 50 * m_outputBytesPerFrame * m_sampleRate / 1000;
456 // DirectSound buffer holds 4 fragments = 200ms worth of samples
457 m_soundcardBufferSize = m_fragmentSize << 2;
458
459 LOG(VB_AUDIO, LOG_INFO, LOC + QString("DirectSound buffer size: %1").arg(m_soundcardBufferSize));
460
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;
467 AudioOutputSettings::FormatToBits(m_outputFormat);
468
469 if (m_UseSPDIF)
470 {
471 wf.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
472 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
473 }
474 else if (m_outputFormat == FORMAT_FLT)
475 {
476 wf.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
477 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
478 }
479 else
480 {
481 wf.Format.wFormatTag = WAVE_FORMAT_PCM;
482 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
483 }
484
485 LOG(VB_AUDIO, LOG_INFO, LOC + QString("New format: %1bits %2ch %3Hz %4")
486 .arg(wf.Samples.wValidBitsPerSample).arg(m_channels)
487 .arg(m_sampleRate).arg(m_UseSPDIF ? "data" : "PCM"));
488
489 if (m_channels <= 2)
490 wf.Format.cbSize = 0;
491 else
492 {
493 wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
494 wf.dwChannelMask = 0x003F; // 0x003F = 5.1 channels
495 wf.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
496 }
497
498 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
499 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
500 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 // Better position accuracy
501 | DSBCAPS_GLOBALFOCUS // Allows background playing
502 | DSBCAPS_LOCHARDWARE; // Needed for 5.1 on emu101k
503
504 if (!m_UseSPDIF)
505 dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME; // Allow volume control
506
507 dsbdesc.dwBufferBytes = m_soundcardBufferSize; // buffer size
508 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wf;
509
510 if (FAILED(IDirectSound_CreateSoundBuffer(m_priv->m_dsobject, &dsbdesc,
511 &m_priv->m_dsbuffer, nullptr)))
512 {
513 /* Vista does not support hardware mixing
514 try without DSBCAPS_LOCHARDWARE */
515 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
516 HRESULT dsresult =
517 IDirectSound_CreateSoundBuffer(m_priv->m_dsobject, &dsbdesc,
518 &m_priv->m_dsbuffer, nullptr);
519 if (FAILED(dsresult))
520 {
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));
524 else
525 Error(QObject::tr("Failed to create DS buffer 0x%1")
526 .arg((DWORD)dsresult, 0, 16));
527 return false;
528 }
529 LOG(VB_AUDIO, LOG_INFO, LOC + "Using software mixer");
530 }
531 LOG(VB_AUDIO, LOG_INFO, LOC + "Created DirectSound buffer");
532
533 return true;
534}
535
537{
538 if (m_priv->m_dsbuffer)
539 m_priv->DestroyDSBuffer();
540}
541
542void AudioOutputDX::WriteAudio(unsigned char * buffer, int size)
543{
544 if (size == 0)
545 return;
546
547 m_priv->FillBuffer(buffer, size);
548 if (!m_priv->m_playStarted)
549 m_priv->StartPlayback();
550}
551
553{
554 if (!m_priv->m_playStarted)
555 return 0;
556
557 HRESULT dsresult;
558 DWORD play_pos, write_pos;
559 int buffered;
560
561 dsresult = IDirectSoundBuffer_GetCurrentPosition(m_priv->m_dsbuffer,
562 &play_pos, &write_pos);
563 if (dsresult != DS_OK)
564 {
565 LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot get current buffer position");
566 return 0;
567 }
568
569 buffered = (int)m_priv->m_writeCursor - (int)play_pos;
570
571 if (buffered <= 0)
572 buffered += m_soundcardBufferSize;
573
574 return buffered;
575}
576
577int AudioOutputDX::GetVolumeChannel([[maybe_unused]] int channel) const
578{
579 HRESULT dsresult;
580 long dxVolume = 0;
581 int volume;
582
583 if (m_UseSPDIF)
584 return 100;
585
586 dsresult = IDirectSoundBuffer_GetVolume(m_priv->m_dsbuffer, &dxVolume);
587 volume = (int)(pow(10,(float)dxVolume/20)*100);
588
589 if (dsresult != DS_OK)
590 {
591 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to get volume %1").arg(dxVolume));
592 return volume;
593 }
594
595 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Got volume %1").arg(volume));
596 return volume;
597}
598
599void AudioOutputDX::SetVolumeChannel([[maybe_unused]] int channel, int volume)
600{
601 HRESULT dsresult;
602 long dxVolume { DSBVOLUME_MIN };
603 if (volume > 0)
604 {
605 float dbAtten = 20 * log10((float)volume/100.F);
606 dxVolume = (long)(100.0F * dbAtten);
607 }
608
609 if (m_UseSPDIF)
610 return;
611
612 // dxVolume is attenuation in 100ths of a decibel
613 dsresult = IDirectSoundBuffer_SetVolume(m_priv->m_dsbuffer, dxVolume);
614
615 if (dsresult != DS_OK)
616 {
617 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to set volume %1").arg(dxVolume));
618 return;
619 }
620
621 LOG(VB_AUDIO, LOG_INFO, LOC + QString("Set volume %1").arg(dxVolume));
622}
623
624QMap<int, QString> *AudioOutputDX::GetDXDevices(void)
625{
626 AudioOutputDXPrivate *tmp_priv = new AudioOutputDXPrivate(nullptr);
627 tmp_priv->InitDirectSound(false);
628 QMap<int, QString> *dxdevs = new QMap<int, QString>(tmp_priv->m_device_list);
629 delete tmp_priv;
630 return dxdevs;
631}
632
633/* 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: freesurround.h:24
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
def error(message)
Definition: smolt.py:409
union WAVEFORMATEXTENSIBLE::@2 Samples