00001 #include <iostream>
00002 #include <cmath>
00003
00004 using namespace std;
00005
00006 #include "mythlogging.h"
00007 #include "audiooutputdx.h"
00008
00009 #include <windows.h>
00010 #include <mmsystem.h>
00011 #include <dsound.h>
00012 #include <unistd.h>
00013
00014 #define LOC QString("AODX: ")
00015
00016 #include <initguid.h>
00017 DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0,
00018 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
00019
00020 #ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
00021 #define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
00022 #endif
00023
00024 #ifndef WAVE_FORMAT_IEEE_FLOAT
00025 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
00026 #endif
00027
00028 #ifndef WAVE_FORMAT_EXTENSIBLE
00029 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
00030 #endif
00031
00032 #ifndef _WAVEFORMATEXTENSIBLE_
00033 typedef struct {
00034 WAVEFORMATEX Format;
00035 union {
00036 WORD wValidBitsPerSample;
00037 WORD wSamplesPerBlock;
00038 WORD wReserved;
00039 } Samples;
00040 DWORD dwChannelMask;
00041 GUID SubFormat;
00042 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
00043 #endif
00044
00045 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT,
00046 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
00047 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM,
00048 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
00049 DEFINE_GUID(_KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF,
00050 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
00051
00052 class AudioOutputDXPrivate
00053 {
00054 public:
00055 AudioOutputDXPrivate(AudioOutputDX *in_parent) :
00056 parent(in_parent),
00057 dsound_dll(NULL),
00058 dsobject(NULL),
00059 dsbuffer(NULL),
00060 playStarted(false),
00061 writeCursor(0),
00062 chosenGUID(NULL),
00063 device_count(0),
00064 device_num(0)
00065 {
00066 }
00067
00068 ~AudioOutputDXPrivate()
00069 {
00070 DestroyDSBuffer();
00071
00072 if (dsobject)
00073 IDirectSound_Release(dsobject);
00074
00075 if (dsound_dll)
00076 FreeLibrary(dsound_dll);
00077 }
00078
00079 int InitDirectSound(bool passthrough = false);
00080 void ResetDirectSound(void);
00081 void DestroyDSBuffer(void);
00082 void FillBuffer(unsigned char *buffer, int size);
00083 bool StartPlayback(void);
00084 static int CALLBACK DSEnumCallback(LPGUID lpGuid,
00085 LPCSTR lpcstrDesc,
00086 LPCSTR lpcstrModule,
00087 LPVOID lpContext);
00088
00089 public:
00090 AudioOutputDX *parent;
00091 HINSTANCE dsound_dll;
00092 LPDIRECTSOUND dsobject;
00093 LPDIRECTSOUNDBUFFER dsbuffer;
00094 bool playStarted;
00095 DWORD writeCursor;
00096 GUID deviceGUID, *chosenGUID;
00097 int device_count, device_num;
00098 QString device_name;
00099 QMap<int, QString> device_list;
00100 };
00101
00102
00103 AudioOutputDX::AudioOutputDX(const AudioSettings &settings) :
00104 AudioOutputBase(settings),
00105 m_priv(new AudioOutputDXPrivate(this)),
00106 m_UseSPDIF(settings.use_passthru)
00107 {
00108 timeBeginPeriod(1);
00109 InitSettings(settings);
00110 if (passthru_device == "auto" || passthru_device.toLower() == "default")
00111 passthru_device = main_device;
00112 else
00113 m_discretedigital = true;
00114 if (settings.init)
00115 Reconfigure(settings);
00116 }
00117
00118 AudioOutputDX::~AudioOutputDX()
00119 {
00120 KillAudio();
00121 if (m_priv)
00122 {
00123 delete m_priv;
00124 m_priv = NULL;
00125 }
00126 timeEndPeriod(1);
00127 }
00128
00129 typedef HRESULT (WINAPI *LPFNDSC) (LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
00130 typedef HRESULT (WINAPI *LPFNDSE) (LPDSENUMCALLBACK, LPVOID);
00131
00132 int CALLBACK AudioOutputDXPrivate::DSEnumCallback(LPGUID lpGuid,
00133 LPCSTR lpcstrDesc,
00134 LPCSTR lpcstrModule,
00135 LPVOID lpContext)
00136 {
00137 const QString enum_desc = lpcstrDesc;
00138 AudioOutputDXPrivate *context = static_cast<AudioOutputDXPrivate*>(lpContext);
00139 const QString cfg_desc = context->device_name;
00140 const int device_num = context->device_num;
00141 const int device_count = context->device_count;
00142
00143 VBAUDIO(QString("Device %1:" + enum_desc).arg(device_count));
00144
00145 if ((device_num == device_count ||
00146 (device_num == 0 && !cfg_desc.isEmpty() &&
00147 enum_desc.startsWith(cfg_desc, Qt::CaseInsensitive))) && lpGuid)
00148 {
00149 context->deviceGUID = *lpGuid;
00150 context->chosenGUID =
00151 &(context->deviceGUID);
00152 context->device_name = enum_desc;
00153 context->device_num = device_count;
00154 }
00155
00156 context->device_list.insert(device_count, enum_desc);
00157 context->device_count++;
00158 return 1;
00159 }
00160
00161 void AudioOutputDXPrivate::ResetDirectSound(void)
00162 {
00163 DestroyDSBuffer();
00164
00165 if (dsobject)
00166 {
00167 IDirectSound_Release(dsobject);
00168 dsobject = NULL;
00169 }
00170
00171 if (dsound_dll)
00172 {
00173 FreeLibrary(dsound_dll);
00174 dsound_dll = NULL;
00175 }
00176
00177 chosenGUID = NULL;
00178 device_count = 0;
00179 device_num = 0;
00180 device_list.clear();
00181 }
00182
00183 int AudioOutputDXPrivate::InitDirectSound(bool passthrough)
00184 {
00185 LPFNDSC OurDirectSoundCreate;
00186 LPFNDSE OurDirectSoundEnumerate;
00187 bool ok;
00188
00189 ResetDirectSound();
00190
00191 dsound_dll = LoadLibrary("DSOUND.DLL");
00192 if (dsound_dll == NULL)
00193 {
00194 VBERROR("Cannot open DSOUND.DLL");
00195 goto error;
00196 }
00197
00198 if (parent)
00199 device_name = passthrough ?
00200 parent->passthru_device : parent->main_device;
00201 device_name = device_name.section(':', 1);
00202 device_num = device_name.toInt(&ok, 10);
00203
00204 VBAUDIO(QString("Looking for device num:%1 or name:%2")
00205 .arg(device_num).arg(device_name));
00206
00207 OurDirectSoundEnumerate =
00208 (LPFNDSE)GetProcAddress(dsound_dll, "DirectSoundEnumerateA");
00209
00210 if(OurDirectSoundEnumerate)
00211 if(FAILED(OurDirectSoundEnumerate(DSEnumCallback, this)))
00212 VBERROR("DirectSoundEnumerate FAILED");
00213
00214 if (!chosenGUID)
00215 {
00216 device_num = 0;
00217 device_name = "Primary Sound Driver";
00218 }
00219
00220 VBAUDIO(QString("Using device %1:%2").arg(device_num).arg(device_name));
00221
00222 OurDirectSoundCreate =
00223 (LPFNDSC)GetProcAddress(dsound_dll, "DirectSoundCreate");
00224
00225 if (OurDirectSoundCreate == NULL)
00226 {
00227 VBERROR("GetProcAddress FAILED");
00228 goto error;
00229 }
00230
00231 if (FAILED(OurDirectSoundCreate(chosenGUID, &dsobject, NULL)))
00232 {
00233 VBERROR("Cannot create a direct sound device");
00234 goto error;
00235 }
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247 if (FAILED(IDirectSound_SetCooperativeLevel(dsobject, GetDesktopWindow(),
00248 DSSCL_EXCLUSIVE)))
00249 VBERROR("Cannot set DS cooperative level");
00250
00251 VBAUDIO("Initialised DirectSound");
00252
00253 return 0;
00254
00255 error:
00256 dsobject = NULL;
00257 if (dsound_dll)
00258 {
00259 FreeLibrary(dsound_dll);
00260 dsound_dll = NULL;
00261 }
00262 return -1;
00263 }
00264
00265 void AudioOutputDXPrivate::DestroyDSBuffer(void)
00266 {
00267 if (!dsbuffer)
00268 return;
00269
00270 VBAUDIO("Destroying DirectSound buffer");
00271 IDirectSoundBuffer_Stop(dsbuffer);
00272 writeCursor = 0;
00273 IDirectSoundBuffer_SetCurrentPosition(dsbuffer, writeCursor);
00274 playStarted = false;
00275 IDirectSoundBuffer_Release(dsbuffer);
00276 dsbuffer = NULL;
00277 }
00278
00279 void AudioOutputDXPrivate::FillBuffer(unsigned char *buffer, int size)
00280 {
00281 void *p_write_position, *p_wrap_around;
00282 DWORD l_bytes1, l_bytes2, play_pos, write_pos;
00283 HRESULT dsresult;
00284
00285 if (!dsbuffer)
00286 return;
00287
00288 while (true)
00289 {
00290
00291 dsresult = IDirectSoundBuffer_GetCurrentPosition(dsbuffer,
00292 &play_pos, &write_pos);
00293 if (dsresult != DS_OK)
00294 {
00295 VBERROR("Cannot get current buffer position");
00296 return;
00297 }
00298
00299 VBAUDIOTS(QString("play: %1 write: %2 wcursor: %3")
00300 .arg(play_pos).arg(write_pos).arg(writeCursor));
00301
00302 while ((writeCursor < write_pos &&
00303 ((writeCursor >= play_pos && write_pos >= play_pos) ||
00304 (writeCursor < play_pos && write_pos < play_pos))) ||
00305 (writeCursor >= play_pos && write_pos < play_pos))
00306 {
00307 VBERROR("buffer underrun");
00308 writeCursor += size;
00309 while (writeCursor >= (DWORD)parent->soundcard_buffer_size)
00310 writeCursor -= parent->soundcard_buffer_size;
00311 }
00312
00313 if ((writeCursor < play_pos && writeCursor + size >= play_pos) ||
00314 (writeCursor >= play_pos &&
00315 writeCursor + size >= play_pos + parent->soundcard_buffer_size))
00316 {
00317 usleep(50000);
00318 continue;
00319 }
00320
00321 break;
00322 }
00323
00324 dsresult = IDirectSoundBuffer_Lock(
00325 dsbuffer,
00326 writeCursor,
00327 size,
00328 &p_write_position,
00329 &l_bytes1,
00330 &p_wrap_around,
00331 &l_bytes2,
00332 0);
00333
00334 if (dsresult == DSERR_BUFFERLOST)
00335 {
00336 IDirectSoundBuffer_Restore(dsbuffer);
00337 dsresult = IDirectSoundBuffer_Lock(dsbuffer, writeCursor, size,
00338 &p_write_position, &l_bytes1,
00339 &p_wrap_around, &l_bytes2, 0);
00340 }
00341
00342 if (dsresult != DS_OK)
00343 {
00344 VBERROR("Cannot lock buffer, audio dropped");
00345 return;
00346 }
00347
00348 memcpy(p_write_position, buffer, l_bytes1);
00349 if (p_wrap_around)
00350 memcpy(p_wrap_around, buffer + l_bytes1, l_bytes2);
00351
00352 writeCursor += l_bytes1 + l_bytes2;
00353
00354 while (writeCursor >= (DWORD)parent->soundcard_buffer_size)
00355 writeCursor -= parent->soundcard_buffer_size;
00356
00357 IDirectSoundBuffer_Unlock(dsbuffer, p_write_position, l_bytes1,
00358 p_wrap_around, l_bytes2);
00359 }
00360
00361 bool AudioOutputDXPrivate::StartPlayback(void)
00362 {
00363 HRESULT dsresult;
00364
00365 dsresult = IDirectSoundBuffer_Play(dsbuffer, 0, 0, DSBPLAY_LOOPING);
00366 if (dsresult == DSERR_BUFFERLOST)
00367 {
00368 IDirectSoundBuffer_Restore(dsbuffer);
00369 dsresult = IDirectSoundBuffer_Play(dsbuffer, 0, 0, DSBPLAY_LOOPING);
00370 }
00371 if (dsresult != DS_OK)
00372 {
00373 VBERROR("Cannot start playing buffer");
00374 playStarted = false;
00375 return false;
00376 }
00377
00378 playStarted=true;
00379 return true;
00380 }
00381
00382 AudioOutputSettings* AudioOutputDX::GetOutputSettings(bool passthrough)
00383 {
00384 AudioOutputSettings *settings = new AudioOutputSettings();
00385 DSCAPS devcaps;
00386 devcaps.dwSize = sizeof(DSCAPS);
00387
00388 m_priv->InitDirectSound(passthrough);
00389 if ((!m_priv->dsobject || !m_priv->dsound_dll) ||
00390 FAILED(IDirectSound_GetCaps(m_priv->dsobject, &devcaps)) )
00391 {
00392 delete settings;
00393 return NULL;
00394 }
00395
00396 VBAUDIO(QString("GetCaps sample rate min: %1 max: %2")
00397 .arg(devcaps.dwMinSecondarySampleRate)
00398 .arg(devcaps.dwMaxSecondarySampleRate));
00399
00400
00401
00402 while (DWORD rate = (DWORD)settings->GetNextRate())
00403 if((rate >= devcaps.dwMinSecondarySampleRate) ||
00404 (rate <= devcaps.dwMaxSecondarySampleRate))
00405 settings->AddSupportedRate(rate);
00406
00407
00408
00409 if (devcaps.dwFlags & DSCAPS_PRIMARY8BIT)
00410 settings->AddSupportedFormat(FORMAT_U8);
00411 if (devcaps.dwFlags & DSCAPS_PRIMARY16BIT)
00412 settings->AddSupportedFormat(FORMAT_S16);
00413 settings->AddSupportedFormat(FORMAT_S24);
00414 settings->AddSupportedFormat(FORMAT_S32);
00415 settings->AddSupportedFormat(FORMAT_FLT);
00416
00417
00418
00419 for (uint i = 2; i < 7; i++)
00420 settings->AddSupportedChannels(i);
00421
00422 settings->setPassthrough(0);
00423
00424 return settings;
00425 }
00426
00427 bool AudioOutputDX::OpenDevice(void)
00428 {
00429 WAVEFORMATEXTENSIBLE wf;
00430 DSBUFFERDESC dsbdesc;
00431
00432 CloseDevice();
00433
00434 m_UseSPDIF = passthru || enc;
00435 m_priv->InitDirectSound(m_UseSPDIF);
00436 if (!m_priv->dsobject || !m_priv->dsound_dll)
00437 {
00438 Error("DirectSound initialization failed");
00439 return false;
00440 }
00441
00442
00443 fragment_size = 50 * output_bytes_per_frame * samplerate / 1000;
00444
00445 soundcard_buffer_size = fragment_size << 2;
00446
00447 VBAUDIO(QString("DirectSound buffer size: %1").arg(soundcard_buffer_size));
00448
00449 wf.Format.nChannels = channels;
00450 wf.Format.nSamplesPerSec = samplerate;
00451 wf.Format.nBlockAlign = output_bytes_per_frame;
00452 wf.Format.nAvgBytesPerSec = samplerate * output_bytes_per_frame;
00453 wf.Format.wBitsPerSample = (output_bytes_per_frame << 3) / channels;
00454 wf.Samples.wValidBitsPerSample =
00455 AudioOutputSettings::FormatToBits(output_format);
00456
00457 if (m_UseSPDIF)
00458 {
00459 wf.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
00460 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
00461 }
00462 else if (output_format == FORMAT_FLT)
00463 {
00464 wf.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
00465 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
00466 }
00467 else
00468 {
00469 wf.Format.wFormatTag = WAVE_FORMAT_PCM;
00470 wf.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
00471 }
00472
00473 VBAUDIO(QString("New format: %1bits %2ch %3Hz %4")
00474 .arg(wf.Samples.wValidBitsPerSample).arg(channels)
00475 .arg(samplerate).arg(m_UseSPDIF ? "data" : "PCM"));
00476
00477 if (channels <= 2)
00478 wf.Format.cbSize = 0;
00479 else
00480 {
00481 wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
00482 wf.dwChannelMask = 0x003F;
00483 wf.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
00484 }
00485
00486 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
00487 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
00488 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2
00489 | DSBCAPS_GLOBALFOCUS
00490 | DSBCAPS_LOCHARDWARE;
00491
00492 if (!m_UseSPDIF)
00493 dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME;
00494
00495 dsbdesc.dwBufferBytes = soundcard_buffer_size;
00496 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wf;
00497
00498 if (FAILED(IDirectSound_CreateSoundBuffer(m_priv->dsobject, &dsbdesc,
00499 &m_priv->dsbuffer, NULL)))
00500 {
00501
00502
00503 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
00504 HRESULT dsresult =
00505 IDirectSound_CreateSoundBuffer(m_priv->dsobject, &dsbdesc,
00506 &m_priv->dsbuffer, NULL);
00507 if (FAILED(dsresult))
00508 {
00509 if (dsresult == DSERR_UNSUPPORTED)
00510 Error(QString("Unsupported format for device %1:%2")
00511 .arg(m_priv->device_num).arg(m_priv->device_name));
00512 else
00513 Error(QString("Failed to create DS buffer 0x%1")
00514 .arg((DWORD)dsresult, 0, 16));
00515 return false;
00516 }
00517 VBAUDIO("Using software mixer");
00518 }
00519 VBAUDIO("Created DirectSound buffer");
00520
00521 return true;
00522 }
00523
00524 void AudioOutputDX::CloseDevice(void)
00525 {
00526 if (m_priv->dsbuffer)
00527 m_priv->DestroyDSBuffer();
00528 }
00529
00530 void AudioOutputDX::WriteAudio(unsigned char * buffer, int size)
00531 {
00532 if (size == 0)
00533 return;
00534
00535 m_priv->FillBuffer(buffer, size);
00536 if (!m_priv->playStarted)
00537 m_priv->StartPlayback();
00538 }
00539
00540 int AudioOutputDX::GetBufferedOnSoundcard(void) const
00541 {
00542 if (!m_priv->playStarted)
00543 return 0;
00544
00545 HRESULT dsresult;
00546 DWORD play_pos, write_pos;
00547 int buffered;
00548
00549 dsresult = IDirectSoundBuffer_GetCurrentPosition(m_priv->dsbuffer,
00550 &play_pos, &write_pos);
00551 if (dsresult != DS_OK)
00552 {
00553 VBERROR("Cannot get current buffer position");
00554 return 0;
00555 }
00556
00557 buffered = (int)m_priv->writeCursor - (int)play_pos;
00558
00559 if (buffered <= 0)
00560 buffered += soundcard_buffer_size;
00561
00562 return buffered;
00563 }
00564
00565 int AudioOutputDX::GetVolumeChannel(int channel) const
00566 {
00567 HRESULT dsresult;
00568 long dxVolume = 0;
00569 int volume;
00570
00571 if (m_UseSPDIF)
00572 return 100;
00573
00574 dsresult = IDirectSoundBuffer_GetVolume(m_priv->dsbuffer, &dxVolume);
00575 volume = (int)(pow(10,(float)dxVolume/20)*100);
00576
00577 if (dsresult != DS_OK)
00578 {
00579 VBERROR(QString("Failed to get volume %1").arg(dxVolume));
00580 return volume;
00581 }
00582
00583 VBAUDIO(QString("Got volume %1").arg(volume));
00584 return volume;
00585 }
00586
00587 void AudioOutputDX::SetVolumeChannel(int channel, int volume)
00588 {
00589 HRESULT dsresult;
00590 float dbAtten = 20 * log10((float)volume/100);
00591 long dxVolume = (volume == 0) ? DSBVOLUME_MIN : (long)(100.0f * dbAtten);
00592
00593 if (m_UseSPDIF)
00594 return;
00595
00596
00597 dsresult = IDirectSoundBuffer_SetVolume(m_priv->dsbuffer, dxVolume);
00598
00599 if (dsresult != DS_OK)
00600 {
00601 VBERROR(QString("Failed to set volume %1").arg(dxVolume));
00602 return;
00603 }
00604
00605 VBAUDIO(QString("Set volume %1").arg(dxVolume));
00606 }
00607
00608 QMap<int, QString> *AudioOutputDX::GetDXDevices(void)
00609 {
00610 AudioOutputDXPrivate *tmp_priv = new AudioOutputDXPrivate(NULL);
00611 tmp_priv->InitDirectSound(false);
00612 QMap<int, QString> *dxdevs = new QMap<int, QString>(tmp_priv->device_list);
00613 delete tmp_priv;
00614 return dxdevs;
00615 }
00616
00617