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