MythTV  0.26-pre
audiooutputdx.cpp
Go to the documentation of this file.
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;       // bits of precision
00037         WORD wSamplesPerBlock;          // valid if wBitsPerSample==0
00038         WORD wReserved;                 // If neither applies, set to zero
00039     } Samples;
00040     DWORD        dwChannelMask;         // which channels are present in stream
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)  // parent can be NULL only when called from GetDXDevices()
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     /* Set DirectSound Cooperative level, ie what control we want over Windows
00238      * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
00239      * settings of the primary buffer, but also that only the sound of our
00240      * application will be hearable when it will have the focus.
00241      * !!! (this is not really working as intended yet because to set the
00242      * cooperative level you need the window handle of your application, and
00243      * I don't know of any easy way to get it. Especially since we might play
00244      * sound without any video, and so what window handle should we use ???
00245      * The hack for now is to use the Desktop window handle - it seems to be
00246      * working */
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,              /* DS buffer */
00326                    writeCursor,           /* Start offset */
00327                    size,                  /* Number of bytes */
00328                    &p_write_position,     /* Address of lock start */
00329                    &l_bytes1,             /* Bytes locked before wrap */
00330                    &p_wrap_around,        /* Buffer address (if wrap around) */
00331                    &l_bytes2,             /* Count of bytes after wrap around */
00332                    0);                    /* Flags */
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     /* We shouldn't assume we can do everything between min and max but
00401        there's no way to test individual rates, so we'll have to */
00402     while (DWORD rate = (DWORD)settings->GetNextRate())
00403         if((rate >= devcaps.dwMinSecondarySampleRate) ||
00404            (rate <= devcaps.dwMaxSecondarySampleRate))
00405             settings->AddSupportedRate(rate);
00406 
00407     /* We can only test for 8 and 16 bit support, DS uses float internally
00408        Guess that we can support S24 and S32 too */
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     /* No way to test anything other than mono or stereo, guess that we can do
00418        up to 5.1 */
00419     for (uint i = 2; i < 7; i++)
00420         settings->AddSupportedChannels(i);
00421 
00422     settings->setPassthrough(0); // Maybe passthrough
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     // fragments are 50ms worth of samples
00443     fragment_size = 50 * output_bytes_per_frame * samplerate / 1000;
00444     // DirectSound buffer holds 4 fragments = 200ms worth of samples
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; // 0x003F = 5.1 channels
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  // Better position accuracy
00489                     | DSBCAPS_GLOBALFOCUS          // Allows background playing
00490                     | DSBCAPS_LOCHARDWARE;         // Needed for 5.1 on emu101k
00491 
00492     if (!m_UseSPDIF)
00493         dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME;     // Allow volume control
00494 
00495     dsbdesc.dwBufferBytes = soundcard_buffer_size; // buffer size
00496     dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wf;
00497 
00498     if (FAILED(IDirectSound_CreateSoundBuffer(m_priv->dsobject, &dsbdesc,
00499                                             &m_priv->dsbuffer, NULL)))
00500     {
00501         /* Vista does not support hardware mixing
00502            try without DSBCAPS_LOCHARDWARE */
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     // dxVolume is attenuation in 100ths of a decibel
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 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends