home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 275 / DPCS0111DVD.ISO / Toolkit / Audio-Visual / VirtualDub / Source / VirtualDub-1.9.10-src.7z / src / Riza / audioout.cpp next >
Encoding:
C/C++ Source or Header  |  2009-09-14  |  17.4 KB  |  779 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #define INITGUID
  19. #include <windows.h>
  20. #include <mmsystem.h>
  21. #include <cguid.h>
  22. #include <dsound.h>
  23.  
  24. #include <vd2/system/math.h>
  25. #include <vd2/system/text.h>
  26. #include <vd2/system/VDString.h>
  27. #include <vd2/Riza/audioout.h>
  28.  
  29. extern HINSTANCE g_hInst;
  30.  
  31. class VDAudioOutputWaveOutW32 : public IVDAudioOutput {
  32. public:
  33.     VDAudioOutputWaveOutW32();
  34.     ~VDAudioOutputWaveOutW32();
  35.  
  36.     bool    Init(uint32 bufsize, uint32 bufcount, const tWAVEFORMATEX *wf, const wchar_t *preferredDevice);
  37.     void    Shutdown();
  38.     void    GoSilent();
  39.  
  40.     bool    IsSilent();
  41.     bool    IsFrozen();
  42.     uint32    GetAvailSpace();
  43.     uint32    GetBufferLevel();
  44.     sint32    GetPosition();
  45.     sint32    GetPositionBytes();
  46.     double    GetPositionTime();
  47.  
  48.     bool    Start();
  49.     bool    Stop();
  50.     bool    Flush();
  51.  
  52.     bool    Write(const void *data, uint32 len);
  53.     bool    Finalize(uint32 timeout);
  54.  
  55. private:
  56.     bool    CheckBuffers();
  57.     bool    WaitBuffers(uint32 timeout);
  58.  
  59.     uint32    mBlockHead;
  60.     uint32    mBlockTail;
  61.     uint32    mBlockWriteOffset;
  62.     uint32    mBlocksPending;
  63.     uint32    mBlockSize;
  64.     uint32    mBlockCount;
  65.     vdblock<char> mBuffer;
  66.     vdblock<WAVEHDR> mHeaders;
  67.  
  68.     HWAVEOUT__ *mhWaveOut;
  69.     void *    mhWaveEvent;
  70.     uint32    mSamplesPerSec;
  71.     uint32    mAvgBytesPerSec;
  72.     VDCriticalSection    mcsWaveDevice;
  73.  
  74.     enum InitState {
  75.         kStateNone        = 0,
  76.         kStateOpened    = 1,
  77.         kStatePlaying    = 2,
  78.         kStateSilent    = 10,
  79.     } mCurState;
  80. };
  81.  
  82. IVDAudioOutput *VDCreateAudioOutputWaveOutW32() {
  83.     return new VDAudioOutputWaveOutW32;
  84. }
  85.  
  86. VDAudioOutputWaveOutW32::VDAudioOutputWaveOutW32()
  87.     : mBlockHead(0)
  88.     , mBlockTail(0)
  89.     , mBlockWriteOffset(0)
  90.     , mBlocksPending(0)
  91.     , mBlockSize(0)
  92.     , mBlockCount(0)
  93.     , mhWaveOut(NULL)
  94.     , mhWaveEvent(NULL)
  95.     , mSamplesPerSec(0)
  96.     , mAvgBytesPerSec(0)
  97.     , mCurState(kStateNone)
  98. {
  99. }
  100.  
  101. VDAudioOutputWaveOutW32::~VDAudioOutputWaveOutW32() {
  102.     Shutdown();
  103. }
  104.  
  105. bool VDAudioOutputWaveOutW32::Init(uint32 bufsize, uint32 bufcount, const WAVEFORMATEX *wf, const wchar_t *preferredDevice) {
  106.     UINT deviceID = WAVE_MAPPER;
  107.  
  108.     if (preferredDevice && *preferredDevice) {
  109.         UINT numDevices = waveOutGetNumDevs();
  110.  
  111.         for(UINT i=0; i<numDevices; ++i) {
  112.             WAVEOUTCAPSA caps = {0};
  113.  
  114.             if (MMSYSERR_NOERROR == waveOutGetDevCapsA(i, &caps, sizeof(caps))) {
  115.                 const VDStringW key(VDTextAToW(caps.szPname).c_str());
  116.  
  117.                 if (key == preferredDevice) {
  118.                     deviceID = i;
  119.                     break;
  120.                 }
  121.             }
  122.         }
  123.     }
  124.  
  125.     mBuffer.resize(bufsize * bufcount);
  126.     mBlockHead = 0;
  127.     mBlockTail = 0;
  128.     mBlockWriteOffset = 0;
  129.     mBlocksPending = 0;
  130.     mBlockSize = bufsize;
  131.     mBlockCount = bufcount;
  132.  
  133.     if (!mhWaveEvent) {
  134.         mhWaveEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  135.  
  136.         if (!mhWaveEvent)
  137.             return false;
  138.     }
  139.  
  140.     MMRESULT res = waveOutOpen(&mhWaveOut, deviceID, wf, (DWORD_PTR)mhWaveEvent, 0, CALLBACK_EVENT);
  141.     if (MMSYSERR_NOERROR != res) {
  142.         Shutdown();
  143.         return false;
  144.     }
  145.  
  146.     mCurState = kStateOpened;
  147.     mSamplesPerSec = wf->nSamplesPerSec;
  148.     mAvgBytesPerSec = wf->nAvgBytesPerSec;
  149.  
  150.     // Hmmm... we can't allocate buffers while the wave device
  151.     // is active...
  152.     mHeaders.resize(bufcount);
  153.     memset(mHeaders.data(), 0, bufcount * sizeof mHeaders[0]);
  154.  
  155.     for(uint32 i=0; i<bufcount; ++i) {
  156.         WAVEHDR& hdr = mHeaders[i];
  157.  
  158.         hdr.dwBufferLength    = bufsize;
  159.         hdr.dwBytesRecorded    = 0;
  160.         hdr.dwFlags            = 0;
  161.         hdr.dwLoops            = 0;
  162.         hdr.dwUser            = 0;
  163.         hdr.lpData            = mBuffer.data() + bufsize * i;
  164.  
  165.         res = waveOutPrepareHeader(mhWaveOut, &hdr, sizeof hdr);
  166.         if (MMSYSERR_NOERROR != res) {
  167.             Shutdown();
  168.             return false;
  169.         }
  170.     }
  171.  
  172.     waveOutPause(mhWaveOut);
  173.     return true;
  174. }
  175.  
  176. void VDAudioOutputWaveOutW32::Shutdown() {
  177.     if (mCurState == kStateSilent)
  178.         return;
  179.  
  180.     Stop();
  181.  
  182.     if (!mHeaders.empty()) {
  183.         for(int i=mHeaders.size()-1; i>=0; --i) {
  184.             WAVEHDR& hdr = mHeaders[i];
  185.  
  186.             if (hdr.dwFlags & WHDR_PREPARED)
  187.                 waveOutUnprepareHeader(mhWaveOut, &hdr, sizeof hdr);
  188.         }
  189.     }
  190.  
  191.     mHeaders.clear();
  192.     mBuffer.clear();
  193.     mBlocksPending = 0;
  194.     mBlockCount = 0;
  195.     mBlockSize = 0;
  196.  
  197.     if (mhWaveOut) {
  198.         waveOutClose(mhWaveOut);
  199.         mhWaveOut = NULL;
  200.     }
  201.  
  202.     if (mhWaveEvent) {
  203.         CloseHandle(mhWaveEvent);
  204.         mhWaveEvent = NULL;
  205.     }
  206.  
  207.     mCurState = kStateNone;
  208. }
  209.  
  210. void VDAudioOutputWaveOutW32::GoSilent() {
  211.     mCurState = kStateSilent;
  212. }
  213.  
  214. bool VDAudioOutputWaveOutW32::IsSilent() {
  215.     return mCurState == kStateSilent;
  216. }
  217.  
  218. bool VDAudioOutputWaveOutW32::Start() {
  219.     if (mCurState == kStateSilent)
  220.         return true;
  221.  
  222.     if (mCurState < kStateOpened)
  223.         return false;
  224.  
  225.     if (MMSYSERR_NOERROR != waveOutRestart(mhWaveOut))
  226.         return false;
  227.  
  228.     mCurState = kStatePlaying;
  229.  
  230.     return true;
  231. }
  232.  
  233. bool VDAudioOutputWaveOutW32::Stop() {
  234.     if (mCurState == kStateSilent) return true;
  235.  
  236.     if (mCurState >= kStateOpened) {
  237.         if (MMSYSERR_NOERROR != waveOutReset(mhWaveOut))
  238.             return false;
  239.  
  240.         mCurState = kStateOpened;
  241.  
  242.         CheckBuffers();
  243.     }
  244.  
  245.     return true;
  246. }
  247.  
  248. bool VDAudioOutputWaveOutW32::CheckBuffers() {
  249.     if (mCurState == kStateSilent) return true;
  250.  
  251.     if (!mBlocksPending)
  252.         return false;
  253.  
  254.     WAVEHDR& hdr = mHeaders[mBlockHead];
  255.  
  256.     if (!(hdr.dwFlags & WHDR_DONE))
  257.         return false;
  258.  
  259.     ++mBlockHead;
  260.     if (mBlockHead >= mBlockCount)
  261.         mBlockHead = 0;
  262.     --mBlocksPending;
  263.     VDASSERT(mBlocksPending >= 0);
  264.     return true;
  265. }
  266.  
  267. bool VDAudioOutputWaveOutW32::WaitBuffers(uint32 timeout) {
  268.     if (mCurState == kStateSilent) return true;
  269.  
  270.     if (mhWaveOut && timeout) {
  271.         for(;;) {
  272.             if (WAIT_OBJECT_0 != WaitForSingleObject(mhWaveEvent, timeout))
  273.                 return false;
  274.  
  275.             if (CheckBuffers())
  276.                 return true;
  277.         }
  278.     }
  279.  
  280.     return CheckBuffers();
  281. }
  282.  
  283. uint32 VDAudioOutputWaveOutW32::GetAvailSpace() {
  284.     CheckBuffers();
  285.     return (mBlockCount - mBlocksPending) * mBlockSize - mBlockWriteOffset;
  286. }
  287.  
  288. uint32 VDAudioOutputWaveOutW32::GetBufferLevel() {
  289.     CheckBuffers();
  290.  
  291.     uint32 level = mBlocksPending * mBlockSize;
  292.     if (mBlockWriteOffset) {
  293.         level -= mBlockSize;
  294.         level += mBlockWriteOffset;
  295.     }
  296.  
  297.     return level;
  298. }
  299.  
  300. bool VDAudioOutputWaveOutW32::Write(const void *data, uint32 len) {
  301.     if (mCurState == kStateSilent)
  302.         return true;
  303.  
  304.     CheckBuffers();
  305.  
  306.     while(len) {
  307.         if (mBlocksPending >= mBlockCount) {
  308.             if (mCurState == kStateOpened) {
  309.                 if (!Start())
  310.                     return false;
  311.             }
  312.  
  313.             if (!WaitBuffers(0)) {
  314.                 if (!WaitBuffers(INFINITE)) {
  315.                     return false;
  316.                 }
  317.                 continue;
  318.             }
  319.             break;
  320.         }
  321.  
  322.         WAVEHDR& hdr = mHeaders[mBlockTail];
  323.  
  324.         uint32 tc = mBlockSize - mBlockWriteOffset;
  325.         if (tc > len)
  326.             tc = len;
  327.  
  328.         if (tc) {
  329.             if (data) {
  330.                 memcpy((char *)hdr.lpData + mBlockWriteOffset, data, tc);
  331.                 data = (const char *)data + tc;
  332.             } else
  333.                 memset((char *)hdr.lpData + mBlockWriteOffset, 0, tc);
  334.  
  335.             mBlockWriteOffset += tc;
  336.             len -= tc;
  337.         }
  338.  
  339.         if (mBlockWriteOffset >= mBlockSize) {
  340.             if (!Flush())
  341.                 return false;
  342.         }
  343.     }
  344.  
  345.     return true;
  346. }
  347.  
  348. bool VDAudioOutputWaveOutW32::Flush() {
  349.     if (mCurState == kStateOpened) {
  350.         if (!Start())
  351.             return false;
  352.     }
  353.  
  354.     if (mBlockWriteOffset <= 0)
  355.         return true;
  356.  
  357.     WAVEHDR& hdr = mHeaders[mBlockTail];
  358.  
  359.     hdr.dwBufferLength = mBlockWriteOffset;
  360.     hdr.dwFlags &= ~WHDR_DONE;
  361.     MMRESULT res = waveOutWrite(mhWaveOut, &hdr, sizeof hdr);
  362.     mBlockWriteOffset = 0;
  363.  
  364.     if (res != MMSYSERR_NOERROR)
  365.         return false;
  366.  
  367.     ++mBlockTail;
  368.     if (mBlockTail >= mBlockCount)
  369.         mBlockTail = 0;
  370.     ++mBlocksPending;
  371.     VDASSERT(mBlocksPending <= mBlockCount);
  372.     return true;
  373. }
  374.  
  375. bool VDAudioOutputWaveOutW32::Finalize(uint32 timeout) {
  376.     if (mCurState == kStateSilent) return true;
  377.  
  378.     Flush();
  379.  
  380.     while(CheckBuffers(), mBlocksPending) {
  381.         if (WAIT_OBJECT_0 != WaitForSingleObject(mhWaveEvent, timeout))
  382.             return false;
  383.     }
  384.  
  385.     return true;
  386. }
  387.  
  388. sint32 VDAudioOutputWaveOutW32::GetPosition() {
  389.     MMTIME mmtime;
  390.  
  391.     if (mCurState != kStatePlaying) return -1;
  392.  
  393.     mmtime.wType = TIME_SAMPLES;
  394.  
  395.     MMRESULT res;
  396.  
  397.     vdsynchronized(mcsWaveDevice) {
  398.         res = waveOutGetPosition(mhWaveOut, &mmtime, sizeof mmtime);
  399.     }
  400.  
  401.     if (MMSYSERR_NOERROR != res)
  402.         return -1;
  403.  
  404.     switch(mmtime.wType) {
  405.     case TIME_BYTES:
  406.         return MulDiv(mmtime.u.cb, 1000, mAvgBytesPerSec);
  407.     case TIME_MS:
  408.         return mmtime.u.ms;
  409.     case TIME_SAMPLES:
  410.         return MulDiv(mmtime.u.sample, 1000, mSamplesPerSec);
  411.     }
  412.  
  413.     return -1;
  414. }
  415.  
  416. sint32 VDAudioOutputWaveOutW32::GetPositionBytes() {
  417.     MMTIME mmtime;
  418.  
  419.     if (mCurState != kStatePlaying) return -1;
  420.  
  421.     mmtime.wType = TIME_BYTES;
  422.  
  423.     MMRESULT res;
  424.  
  425.     vdsynchronized(mcsWaveDevice) {
  426.         res = waveOutGetPosition(mhWaveOut, &mmtime, sizeof mmtime);
  427.     }
  428.  
  429.     if (MMSYSERR_NOERROR != res)
  430.         return -1;
  431.  
  432.     switch(mmtime.wType) {
  433.     case TIME_BYTES:
  434.         return mmtime.u.cb;
  435.     case TIME_MS:
  436.         return MulDiv(mmtime.u.ms, mAvgBytesPerSec, 1000);
  437.     case TIME_SAMPLES:
  438.         return MulDiv(mmtime.u.sample, mAvgBytesPerSec, mSamplesPerSec);
  439.     }
  440.  
  441.     return -1;
  442. }
  443.  
  444. double VDAudioOutputWaveOutW32::GetPositionTime() {
  445.     MMTIME mmtime;
  446.  
  447.     if (mCurState != kStatePlaying) return -1;
  448.  
  449.     mmtime.wType = TIME_MS;
  450.  
  451.     MMRESULT res;
  452.  
  453.     vdsynchronized(mcsWaveDevice) {
  454.         res = waveOutGetPosition(mhWaveOut, &mmtime, sizeof mmtime);
  455.     }
  456.  
  457.     if (MMSYSERR_NOERROR != res)
  458.         return -1;
  459.  
  460.     switch(mmtime.wType) {
  461.     case TIME_BYTES:
  462.         return (double)mmtime.u.cb / (double)mAvgBytesPerSec;
  463.     case TIME_MS:
  464.         return (double)mmtime.u.ms / 1000.0;
  465.     case TIME_SAMPLES:
  466.         return (double)mmtime.u.sample / (double)mSamplesPerSec;
  467.     }
  468.  
  469.     return -1;
  470. }
  471.  
  472. bool VDAudioOutputWaveOutW32::IsFrozen() {
  473.     if (mCurState != kStatePlaying)
  474.         return true;
  475.  
  476.     CheckBuffers();
  477.  
  478.     return !mBlocksPending;
  479. }
  480.  
  481. ///////////////////////////////////////////////////////////////////////////
  482.  
  483. class VDAudioOutputDirectSoundW32 : public IVDAudioOutput {
  484. public:
  485.     VDAudioOutputDirectSoundW32();
  486.     ~VDAudioOutputDirectSoundW32();
  487.  
  488.     bool    Init(uint32 bufsize, uint32 bufcount, const tWAVEFORMATEX *wf, const wchar_t *preferredDevice);
  489.     void    Shutdown();
  490.     void    GoSilent();
  491.  
  492.     bool    IsSilent();
  493.     bool    IsFrozen();
  494.     uint32    GetAvailSpace();
  495.     uint32    GetBufferLevel();
  496.     sint32    GetPosition();
  497.     sint32    GetPositionBytes();
  498.     double    GetPositionTime();
  499.  
  500.     bool    Start();
  501.     bool    Stop();
  502.     bool    Flush();
  503.  
  504.     bool    Write(const void *data, uint32 len);
  505.     bool    Finalize(uint32 timeout);
  506.  
  507. private:
  508.     bool    Init2(uint32 bufsize, uint32 bufcount, const tWAVEFORMATEX *wf);
  509.  
  510.     static ATOM sWndClass;
  511.  
  512.     HWND                mhwnd;
  513.     HMODULE                mhmodDS;
  514.     IDirectSound8        *mpDS8;
  515.     IDirectSoundBuffer8    *mpDSBuffer;
  516.  
  517.     uint32    mBufferSize;
  518.     uint32    mTailCursor;
  519.  
  520.     double    mMillisecsPerByte;
  521.  
  522.     enum InitState {
  523.         kStateNone        = 0,
  524.         kStateOpened    = 1,
  525.         kStatePlaying    = 2,
  526.         kStateSilent    = 10,
  527.     } mCurState;
  528. };
  529.  
  530. ATOM VDAudioOutputDirectSoundW32::sWndClass;
  531.  
  532. IVDAudioOutput *VDCreateAudioOutputDirectSoundW32() {
  533.     return new VDAudioOutputDirectSoundW32;
  534. }
  535.  
  536. VDAudioOutputDirectSoundW32::VDAudioOutputDirectSoundW32()
  537.     : mhwnd(NULL)
  538.     , mhmodDS(NULL)
  539.     , mpDS8(NULL)
  540.     , mpDSBuffer(NULL)
  541. {
  542. }
  543.  
  544. VDAudioOutputDirectSoundW32::~VDAudioOutputDirectSoundW32() {
  545. }
  546.  
  547. bool VDAudioOutputDirectSoundW32::Init(uint32 bufsize, uint32 bufcount, const tWAVEFORMATEX *wf, const wchar_t *preferredDevice) {
  548.     if (!Init2(bufsize, bufcount, wf)) {
  549.         Shutdown();
  550.         return false;
  551.     }
  552.     return true;
  553. }
  554.  
  555. bool VDAudioOutputDirectSoundW32::Init2(uint32 bufsize, uint32 bufcount, const tWAVEFORMATEX *wf) {
  556.     mBufferSize = bufsize * bufcount;
  557.     mMillisecsPerByte = 1000.0 * (double)wf->nBlockAlign / (double)wf->nAvgBytesPerSec;
  558.  
  559.     // attempt to load DirectSound library
  560.     mhmodDS = LoadLibraryA("dsound");
  561.     if (!mhmodDS)
  562.         return false;
  563.  
  564.     typedef HRESULT (WINAPI *tpDirectSoundCreate8)(LPCGUID, LPDIRECTSOUND8 *, LPUNKNOWN);
  565.     tpDirectSoundCreate8 pDirectSoundCreate8 = (tpDirectSoundCreate8)GetProcAddress(mhmodDS, "DirectSoundCreate8");
  566.     if (!pDirectSoundCreate8) {
  567.         VDDEBUG("VDAudioOutputDirectSound: Cannot find DirectSoundCreate8 entry point!\n");
  568.         return false;
  569.     }
  570.  
  571.     // attempt to create DirectSound object
  572.     HRESULT hr = pDirectSoundCreate8(NULL, &mpDS8, NULL);
  573.     if (FAILED(hr)) {
  574.         VDDEBUG("VDAudioOutputDirectSound: Failed to create DirectSound object! hr=%08x\n", hr);
  575.         return false;
  576.     }
  577.  
  578.     // register window class
  579.     if (!sWndClass) {
  580.         WNDCLASS wc = {0};
  581.         wc.lpfnWndProc = DefWindowProc;
  582.         wc.lpszClassName = "VirtualDub DirectSound window";
  583.         wc.hInstance = g_hInst;
  584.         sWndClass = RegisterClass(&wc);
  585.         if (!sWndClass)
  586.             return false;
  587.     }
  588.  
  589.     // create window
  590.     mhwnd = CreateWindowA((LPCTSTR)sWndClass, "", WS_POPUP, 0, 0, 0, 0, NULL, NULL, g_hInst, NULL);
  591.     if (!mhwnd) {
  592.         VDDEBUG("VDAudioOutputDirectSound: Failed to create window!\n");
  593.         return false;
  594.     }
  595.  
  596.     // Set cooperative level.
  597.     //
  598.     // From microsoft.public.win32.programmer.directx.audio, by an SDE on the Windows AV team:
  599.     //
  600.     // "I can't speak for all DirectX components but DirectSound does not
  601.     //  subclass the window procedure.  It simply uses the window handle to
  602.     //  determine (every 1/2 second, in a seperate thread) if the window that
  603.     //  corresponds to the handle has the focus (Actually, it is slightly more
  604.     //  complicated than that, but that is close enough for this discussion). 
  605.     //  You can feel free to use the desktop window or console window for the
  606.     //  window handle if you are going to create GLOBAL_FOCUS buffers."
  607.     //
  608.     // Alright, you guys said we could do it!
  609.     //
  610.     hr = mpDS8->SetCooperativeLevel(GetDesktopWindow(), DSSCL_PRIORITY);
  611.     if (FAILED(hr)) {
  612.         VDDEBUG("VDAudioOutputDirectSound: Failed to set cooperative level! hr=%08x\n", hr);
  613.         return false;
  614.     }
  615.  
  616.     // create looping secondary buffer
  617.     DSBUFFERDESC dsd={sizeof(DSBUFFERDESC)};
  618.     dsd.dwFlags            = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
  619.     dsd.dwBufferBytes    = bufsize * bufcount;
  620.     dsd.lpwfxFormat        = (WAVEFORMATEX *)wf;
  621.     dsd.guid3DAlgorithm    = DS3DALG_DEFAULT;
  622.  
  623.     IDirectSoundBuffer *pDSB;
  624.     hr = mpDS8->CreateSoundBuffer(&dsd, &pDSB, NULL);
  625.     if (FAILED(hr)) {
  626.         VDDEBUG("VDAudioOutputDirectSound: Failed to create secondary buffer! hr=%08x\n", hr);
  627.         return false;
  628.     }
  629.  
  630.     // query to IDirectSoundBuffer8
  631.     hr = pDSB->QueryInterface(IID_IDirectSoundBuffer8, (void **)&mpDSBuffer);
  632.     pDSB->Release();
  633.     if (FAILED(hr)) {
  634.         VDDEBUG("VDAudioOutputDirectSound: Failed to obtain IDirectSoundBuffer8 interface! hr=%08x\n", hr);
  635.         return false;
  636.     }
  637.  
  638.     // all done!
  639.     mTailCursor = 0;
  640.     return true;
  641. }
  642.  
  643. void VDAudioOutputDirectSoundW32::Shutdown() {
  644.     if (mpDSBuffer) {
  645.         mpDSBuffer->Release();
  646.         mpDSBuffer = NULL;
  647.     }
  648.  
  649.     if (mpDS8) {
  650.         mpDS8->Release();
  651.         mpDS8 = NULL;
  652.     }
  653.  
  654.     if (mhmodDS) {
  655.         FreeLibrary(mhmodDS);
  656.         mhmodDS = NULL;
  657.     }
  658.  
  659.     if (mhwnd) {
  660.         DestroyWindow(mhwnd);
  661.         mhwnd = NULL;
  662.     }
  663. }
  664.  
  665. void VDAudioOutputDirectSoundW32::GoSilent() {
  666. }
  667.  
  668. bool VDAudioOutputDirectSoundW32::IsSilent() {
  669.     return !mpDSBuffer;
  670. }
  671.  
  672. bool VDAudioOutputDirectSoundW32::IsFrozen() {
  673.     return false;
  674. }
  675.  
  676. uint32 VDAudioOutputDirectSoundW32::GetAvailSpace() {
  677.     DWORD playCursor, writeCursor;
  678.     HRESULT hr = mpDSBuffer->GetCurrentPosition(&playCursor, &writeCursor);
  679.  
  680.     sint32 space = playCursor - mTailCursor;
  681.     if (space < 0)
  682.         space += mBufferSize;
  683.  
  684.     return (uint32)space;
  685. }
  686.  
  687. uint32 VDAudioOutputDirectSoundW32::GetBufferLevel() {
  688.     return mBufferSize - GetAvailSpace();
  689. }
  690.  
  691. sint32 VDAudioOutputDirectSoundW32::GetPosition() {
  692.     DWORD playCursor;
  693.     HRESULT hr = mpDSBuffer->GetCurrentPosition(&playCursor, NULL);
  694.  
  695.     return VDRoundToInt32(playCursor * mMillisecsPerByte);
  696. }
  697.  
  698. sint32 VDAudioOutputDirectSoundW32::GetPositionBytes() {
  699.     DWORD playCursor;
  700.     HRESULT hr = mpDSBuffer->GetCurrentPosition(&playCursor, NULL);
  701.  
  702.     return playCursor;
  703. }
  704.  
  705. double VDAudioOutputDirectSoundW32::GetPositionTime() {
  706.     return GetPosition() / 1000.0;
  707. }
  708.  
  709. bool VDAudioOutputDirectSoundW32::Start() {
  710.     if (!mpDSBuffer)
  711.         return true;
  712.  
  713.     HRESULT hr = mpDSBuffer->Play(0, 0, DSBPLAY_LOOPING);
  714.  
  715.     return SUCCEEDED(hr);
  716. }
  717.  
  718. bool VDAudioOutputDirectSoundW32::Stop() {
  719.     if (!mpDSBuffer)
  720.         return true;
  721.  
  722.     HRESULT hr = mpDSBuffer->Stop();
  723.     return SUCCEEDED(hr);
  724. }
  725.  
  726. bool VDAudioOutputDirectSoundW32::Flush() {
  727.     return true;
  728. }
  729.  
  730. bool VDAudioOutputDirectSoundW32::Write(const void *data, uint32 len) {
  731.     if (!mpDSBuffer)
  732.         return true;
  733.  
  734.     while(len > 0) {
  735.         DWORD playCursor, writeCursor;
  736.         HRESULT hr = mpDSBuffer->GetCurrentPosition(&playCursor, &writeCursor);
  737.         if (FAILED(hr)) {
  738.             return false;
  739.         }
  740.  
  741.         sint32 tc = (sint32)playCursor - mTailCursor;
  742.         if (tc < 0)
  743.             tc += mBufferSize;
  744.  
  745.         if (!tc) {
  746.             ::Sleep(1);
  747.             continue;
  748.         }
  749.  
  750.         if ((uint32)tc > len)
  751.             tc = len;
  752.  
  753.         LPVOID p1, p2;
  754.         DWORD tc1, tc2;
  755.         hr = mpDSBuffer->Lock(mTailCursor, tc, &p1, &tc1, &p2, &tc2, 0);
  756.         if (FAILED(hr))
  757.             return false;
  758.  
  759.         memcpy(p1, data, tc1);
  760.         data = (char *)data + tc1;
  761.         memcpy(p2, data, tc2);
  762.         data = (char *)data + tc2;
  763.  
  764.         mpDSBuffer->Unlock(p1, tc1, p2, tc2);
  765.  
  766.         len -= tc;
  767.  
  768.         mTailCursor += tc;
  769.         if (mTailCursor >= mBufferSize)
  770.             mTailCursor -= mBufferSize;
  771.     }
  772.  
  773.     return true;
  774. }
  775.  
  776. bool VDAudioOutputDirectSoundW32::Finalize(uint32 timeout) {
  777.     return true;
  778. }
  779.