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 / source / w32audiocodec.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2009-09-14  |  16.5 KB  |  506 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    A/V interface library
  3. //    Copyright (C) 1998-2004 Avery Lee
  4. //
  5. //    This program is free software; you can redistribute it and/or modify
  6. //    it under the terms of the GNU General Public License as published by
  7. //    the Free Software Foundation; either version 2 of the License, or
  8. //    (at your option) any later version.
  9. //
  10. //    This program is distributed in the hope that it will be useful,
  11. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. //    GNU General Public License for more details.
  14. //
  15. //    You should have received a copy of the GNU General Public License
  16. //    along with this program; if not, write to the Free Software
  17. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19. #include <vd2/system/vdtypes.h>
  20. #include <vd2/system/strutil.h>
  21. #include <vd2/system/Error.h>
  22. #include <vd2/system/protscope.h>
  23. #include <vd2/system/vdalloc.h>
  24. #include <vd2/Riza/w32audiocodec.h>
  25.  
  26. namespace {
  27.     // Need to take care of this at some point.
  28.     void SafeCopyWaveFormat(vdstructex<VDWaveFormat>& dst, const VDWaveFormat *src) {
  29.         VDASSERTCT(sizeof(VDWaveFormat) == sizeof(WAVEFORMATEX));
  30.         if (src->mTag == WAVE_FORMAT_PCM) {
  31.             dst.resize(sizeof(VDWaveFormat));
  32.             dst->mExtraSize = 0;
  33.             memcpy(dst.data(), src, sizeof(PCMWAVEFORMAT));
  34.         } else
  35.             dst.assign((const VDWaveFormat *)src, sizeof(VDWaveFormat) + src->mExtraSize);
  36.     }
  37.  
  38.     const char *VDGetNameOfMMSYSTEMErrorW32(MMRESULT res) {
  39.         switch(res) {
  40.         case MMSYSERR_NOERROR:        return "MMSYSERR_NOERROR";
  41.         case MMSYSERR_ERROR:        return "MMSYSERR_ERROR";
  42.         case MMSYSERR_BADDEVICEID:    return "MMSYSERR_BADDEVICEID";
  43.         case MMSYSERR_NOTENABLED:    return "MMSYSERR_NOTENABLED";
  44.         case MMSYSERR_ALLOCATED:    return "MMSYSERR_ALLOCATED";
  45.         case MMSYSERR_INVALHANDLE:    return "MMSYSERR_INVALHANDLE";
  46.         case MMSYSERR_NODRIVER:        return "MMSYSERR_NODRIVER";
  47.         case MMSYSERR_NOMEM:        return "MMSYSERR_NOMEM";
  48.         case MMSYSERR_NOTSUPPORTED:    return "MMSYSERR_NOTSUPPORTED";
  49.         case MMSYSERR_BADERRNUM:    return "MMSYSERR_BADERRNUM";
  50.         case MMSYSERR_INVALFLAG:    return "MMSYSERR_INVALFLAG";
  51.         case MMSYSERR_INVALPARAM:    return "MMSYSERR_INVALPARAM";
  52.         case MMSYSERR_HANDLEBUSY:    return "MMSYSERR_HANDLEBUSY";
  53.         case MMSYSERR_INVALIDALIAS:    return "MMSYSERR_INVALIDALIAS";
  54.         case MMSYSERR_BADDB:        return "MMSYSERR_BADDB";
  55.         case MMSYSERR_KEYNOTFOUND:    return "MMSYSERR_KEYNOTFOUND";
  56.         case MMSYSERR_READERROR:    return "MMSYSERR_READERROR";
  57.         case MMSYSERR_WRITEERROR:    return "MMSYSERR_WRITEERROR";
  58.         case MMSYSERR_DELETEERROR:    return "MMSYSERR_DELETEERROR";
  59.         case MMSYSERR_VALNOTFOUND:    return "MMSYSERR_VALNOTFOUND";
  60.         case MMSYSERR_NODRIVERCB:    return "MMSYSERR_NODRIVERCB";
  61.         case MMSYSERR_MOREDATA:        return "MMSYSERR_MOREDATA";
  62.         default:
  63.             return "unknown";
  64.         }
  65.     }
  66.  
  67.     const char *VDGetNameOfACMErrorW32(MMRESULT res) {
  68.         switch(res) {
  69.             case ACMERR_NOTPOSSIBLE:    return "ACMERR_NOTPOSSIBLE";
  70.             case ACMERR_BUSY:            return "ACMERR_BUSY";
  71.             case ACMERR_UNPREPARED:        return "ACMERR_UNPREPARED";
  72.             case ACMERR_CANCELED:        return "ACMERR_CANCELED";
  73.             default:
  74.                 return VDGetNameOfMMSYSTEMErrorW32(res);
  75.         }
  76.     }
  77. }
  78.  
  79. IVDAudioCodec *VDCreateAudioCompressorW32(const VDWaveFormat *srcFormat, const VDWaveFormat *dstFormat, const char *pShortNameDriverHint) {
  80.     vdautoptr<VDAudioCodecW32> codec(new VDAudioCodecW32);
  81.  
  82.     if (!codec->Init((const WAVEFORMATEX *)srcFormat, (const WAVEFORMATEX *)dstFormat, true, pShortNameDriverHint))
  83.         return NULL;
  84.  
  85.     return codec.release();
  86. }
  87.  
  88. IVDAudioCodec *VDCreateAudioDecompressorW32(const VDWaveFormat *srcFormat, const VDWaveFormat *dstFormat, const char *pShortNameDriverHint) {
  89.     vdautoptr<VDAudioCodecW32> codec(new VDAudioCodecW32);
  90.  
  91.     if (!codec->Init((const WAVEFORMATEX *)srcFormat, (const WAVEFORMATEX *)dstFormat, false, pShortNameDriverHint))
  92.         return NULL;
  93.  
  94.     return codec.release();
  95. }
  96.  
  97. VDAudioCodecW32::VDAudioCodecW32()
  98.     : mhStream(NULL)
  99.     , mOutputReadPt(0)
  100. {
  101.     mDriverName[0] = 0;
  102.     mDriverFilename[0] = 0;
  103. }
  104.  
  105. VDAudioCodecW32::~VDAudioCodecW32() {
  106.     Shutdown();
  107. }
  108.  
  109. bool VDAudioCodecW32::Init(const WAVEFORMATEX *pSrcFormat, const WAVEFORMATEX *pDstFormat, bool isCompression, const char *pDriverShortNameHint) {
  110.     Shutdown();
  111.  
  112.     SafeCopyWaveFormat(mSrcFormat, (const VDWaveFormat *)pSrcFormat);
  113.  
  114.     if (pDstFormat) {
  115.         SafeCopyWaveFormat(mDstFormat, (const VDWaveFormat *)pDstFormat);
  116.     } else {
  117.         VDASSERT(!isCompression);
  118.  
  119.         DWORD dwDstFormatSize;
  120.  
  121.         VDVERIFY(!acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, (LPVOID)&dwDstFormatSize));
  122.  
  123.         mDstFormat.resize(dwDstFormatSize);
  124.         mDstFormat->mTag = WAVE_FORMAT_PCM;
  125.  
  126.         if (acmFormatSuggest(NULL, (WAVEFORMATEX *)pSrcFormat, (WAVEFORMATEX *)mDstFormat.data(), dwDstFormatSize, ACM_FORMATSUGGESTF_WFORMATTAG)) {
  127.             Shutdown();
  128.             return false;
  129.         }
  130.  
  131.         // sanitize the destination format a bit
  132.  
  133.         if (mDstFormat->mSampleBits != 8 && mDstFormat->mSampleBits != 16)
  134.             mDstFormat->mSampleBits = 16;
  135.  
  136.         if (mDstFormat->mChannels != 1 && mDstFormat->mChannels !=2)
  137.             mDstFormat->mChannels = 2;
  138.  
  139.         mDstFormat->mBlockSize        = (uint16)((mDstFormat->mSampleBits >> 3) * mDstFormat->mChannels);
  140.         mDstFormat->mDataRate        = mDstFormat->mBlockSize * mDstFormat->mSamplingRate;
  141.         mDstFormat->mExtraSize        = 0;
  142.         mDstFormat.resize(sizeof(WAVEFORMATEX));
  143.     }
  144.  
  145.     // try to find the hinted driver, if a hint is provided
  146.     struct ACMDriverFinder {
  147.         ACMDriverFinder(const char *hint) : mpHint(hint), mhDriver(NULL) {}
  148.  
  149.         static BOOL CALLBACK Callback(HACMDRIVERID hadid, DWORD_PTR dwInstance, DWORD fdwSupport) {
  150.             ACMDriverFinder *pThis = (ACMDriverFinder *)dwInstance;
  151.  
  152.             ACMDRIVERDETAILS add = {sizeof(ACMDRIVERDETAILS)};
  153.             if (!pThis->mhDriver && !acmDriverDetails(hadid, &add, 0) && !_stricmp(add.szShortName, pThis->mpHint)) {
  154.                 if (!acmDriverOpen(&pThis->mhDriver, hadid, 0))
  155.                     return FALSE;
  156.             }
  157.  
  158.             return TRUE;
  159.         }
  160.  
  161.         const char *const mpHint;
  162.         HACMDRIVER mhDriver;
  163.     };
  164.  
  165.     ACMDriverFinder drvFinder(pDriverShortNameHint);
  166.  
  167.     if (pDriverShortNameHint)
  168.         acmDriverEnum(ACMDriverFinder::Callback, (DWORD_PTR)&drvFinder, 0);
  169.  
  170.     // open conversion stream
  171.  
  172.     MMRESULT res;
  173.  
  174.     memset(&mBufferHdr, 0, sizeof mBufferHdr);    // Do this so we can detect whether the buffer is prepared or not.
  175.  
  176.     for(;;) {
  177.         res = acmStreamOpen(&mhStream, drvFinder.mhDriver, (WAVEFORMATEX *)pSrcFormat, (WAVEFORMATEX *)mDstFormat.data(), NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME);
  178.         if (!res)
  179.             break;
  180.  
  181.         // Aud-X accepts PCM/6ch but not WAVE_FORMAT_EXTENSIBLE/PCM/6ch. Argh. We attempt to work
  182.         // around this by trying a PCM version if WFE doesn't work.
  183.         if (isCompression) {
  184.             // Need to put this somewhere.
  185.             struct WaveFormatExtensibleW32 {
  186.                 WAVEFORMATEX mFormat;
  187.                 union {
  188.                     uint16 mBitDepth;
  189.                     uint16 mSamplesPerBlock;        // may be zero, according to MSDN
  190.                 };
  191.                 uint32    mChannelMask;
  192.                 GUID    mGuid;
  193.             };
  194.  
  195.             static const GUID local_KSDATAFORMAT_SUBTYPE_PCM={    // so we don't have to bring in ksmedia.h
  196.                 WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
  197.             };
  198.  
  199.             if (pSrcFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE && pSrcFormat->cbSize >= sizeof(WaveFormatExtensibleW32) - sizeof(WAVEFORMATEX)) {
  200.                 const WaveFormatExtensibleW32& wfexex = *(const WaveFormatExtensibleW32 *)pSrcFormat;
  201.  
  202.                 if (wfexex.mGuid == local_KSDATAFORMAT_SUBTYPE_PCM) {
  203.                     // Rewrite the format to be straight PCM and try again.
  204.                     vdstructex<VDWaveFormat> srcFormat2(mSrcFormat.data(), sizeof(VDWaveFormat));
  205.                     srcFormat2->mExtraSize    = 0;
  206.                     srcFormat2->mTag        = WAVE_FORMAT_PCM;
  207.                     MMRESULT res2 = acmStreamOpen(&mhStream, drvFinder.mhDriver, (WAVEFORMATEX *)srcFormat2.data(), (WAVEFORMATEX *)mDstFormat.data(), NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME);
  208.  
  209.                     if (!res2) {
  210.                         res = res2;
  211.                         mSrcFormat = srcFormat2;
  212.                         pSrcFormat = (WAVEFORMATEX *)mSrcFormat.data();
  213.                         break;
  214.                     }
  215.                 }
  216.             }
  217.         }
  218.  
  219.         if (!drvFinder.mhDriver)
  220.             break;
  221.  
  222.         acmDriverClose(drvFinder.mhDriver, 0);
  223.         drvFinder.mhDriver = NULL;
  224.     }
  225.  
  226.     if (drvFinder.mhDriver)
  227.         acmDriverClose(drvFinder.mhDriver, 0);
  228.  
  229.     if (res) {
  230.         Shutdown();
  231.  
  232.         if (isCompression) {
  233.             if (res == ACMERR_NOTPOSSIBLE) {
  234.                 throw MyError(
  235.                             "Error initializing audio stream compression:\n"
  236.                             "The audio codec cannot compress the source audio to the desired format.\n"
  237.                             "\n"
  238.                             "Check that the sampling rate and number of channels in the source is compatible with the selected compressed audio format."
  239.                         );
  240.             } else
  241.                 throw MyError("Error initializing audio stream compression.");
  242.         } else {
  243.             if (res == ACMERR_NOTPOSSIBLE) {
  244.                 throw MyError(
  245.                             "Error initializing audio stream decompression:\n"
  246.                             "No installed audio codec could be found to decompress the compressed source audio.\n"
  247.                             "\n"
  248.                             "Check to make sure you have the required codec%s."
  249.                             ,
  250.                             (pSrcFormat->wFormatTag&~1)==0x160 ? " (Microsoft Audio Codec)" : ""
  251.                         );
  252.             } else
  253.                 throw MyError("Error initializing audio stream decompression.");
  254.         }
  255.     }
  256.  
  257.     DWORD dwSrcBufferSize = mSrcFormat->mDataRate / 5;
  258.     DWORD dwDstBufferSize = mDstFormat->mDataRate / 5;
  259.  
  260.     if (!dwSrcBufferSize)
  261.         dwSrcBufferSize = 1;
  262.  
  263.     dwSrcBufferSize += mSrcFormat->mBlockSize - 1;
  264.     dwSrcBufferSize -= dwSrcBufferSize % mSrcFormat->mBlockSize;
  265.  
  266.     if (!dwDstBufferSize)
  267.         dwDstBufferSize = 1;
  268.  
  269.     dwDstBufferSize += mDstFormat->mBlockSize - 1;
  270.     dwDstBufferSize -= dwDstBufferSize % mDstFormat->mBlockSize;
  271.  
  272.     if (acmStreamSize(mhStream, dwSrcBufferSize, &dwDstBufferSize, ACM_STREAMSIZEF_SOURCE)) {
  273.         memset(&mBufferHdr, 0, sizeof mBufferHdr);
  274.         throw MyError("Error initializing audio stream output size.");
  275.     }
  276.  
  277.     mInputBuffer.resize(dwSrcBufferSize);
  278.     mOutputBuffer.resize(dwDstBufferSize);
  279.  
  280.     mBufferHdr.cbStruct        = sizeof(ACMSTREAMHEADER);
  281.     mBufferHdr.pbSrc        = (LPBYTE)&mInputBuffer.front();
  282.     mBufferHdr.cbSrcLength    = mInputBuffer.size();
  283.     mBufferHdr.pbDst        = (LPBYTE)&mOutputBuffer.front();
  284.     mBufferHdr.cbDstLength    = mOutputBuffer.size();
  285.  
  286.     if (acmStreamPrepareHeader(mhStream, &mBufferHdr, 0)) {
  287.         memset(&mBufferHdr, 0, sizeof mBufferHdr);
  288.         throw MyError("Error preparing audio decompression buffers.");
  289.     }
  290.  
  291.     Restart();
  292.  
  293.     // try to get driver name for debugging purposes (OK to fail)
  294.     mDriverName[0] = mDriverFilename[0] = 0;
  295.  
  296.     HACMDRIVERID hDriverID;
  297.     if (!acmDriverID((HACMOBJ)mhStream, &hDriverID, 0)) {
  298.         ACMDRIVERDETAILS add = { sizeof(ACMDRIVERDETAILS) };
  299.         if (!acmDriverDetails(hDriverID, &add, 0)) {
  300.             strncpyz(mDriverName, add.szLongName, sizeof mDriverName);
  301.             strncpyz(mDriverFilename, add.szShortName, sizeof mDriverFilename);
  302.         }
  303.     }
  304.  
  305.     return true;
  306. }
  307.  
  308. void VDAudioCodecW32::Shutdown() {
  309.     mDstFormat.clear();
  310.  
  311.     if (mhStream) {
  312.         if (mBufferHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) {
  313.             mBufferHdr.cbSrcLength = mInputBuffer.size();
  314.             mBufferHdr.cbDstLength = mOutputBuffer.size();
  315.             acmStreamUnprepareHeader(mhStream, &mBufferHdr, 0);
  316.         }
  317.         acmStreamClose(mhStream, 0);
  318.         mhStream = NULL;
  319.     }
  320.  
  321.     mDriverName[0] = 0;
  322.     mDriverFilename[0] = 0;
  323. }
  324.  
  325. void *VDAudioCodecW32::LockInputBuffer(unsigned& bytes) {
  326.     unsigned space = mInputBuffer.size() - mBufferHdr.cbSrcLength;
  327.     VDASSERT((int)space >= 0);
  328.  
  329.     bytes = space;
  330.     return &mInputBuffer[mBufferHdr.cbSrcLength];
  331. }
  332.  
  333. void VDAudioCodecW32::UnlockInputBuffer(unsigned bytes) {
  334.     mBufferHdr.cbSrcLength += bytes;
  335. }
  336.  
  337. void VDAudioCodecW32::Restart() {
  338.     mBufferHdr.cbSrcLength = 0;
  339.     mBufferHdr.cbDstLengthUsed = 0;
  340.     mbFirst    = true;
  341.     mbFlushing = false;
  342.     mbEnded = false;
  343.     mOutputReadPt = 0;
  344. }
  345.  
  346. bool VDAudioCodecW32::Convert(bool flush, bool requireOutput) {
  347.     if (mOutputReadPt < mBufferHdr.cbDstLengthUsed)
  348.         return true;
  349.  
  350.     if (mbEnded)
  351.         return false;
  352.  
  353.     mBufferHdr.cbSrcLengthUsed = 0;
  354.     mBufferHdr.cbDstLengthUsed = 0;
  355.  
  356.     const bool isCompression = mDstFormat->mTag != WAVE_FORMAT_PCM;
  357.  
  358.     // Run the message queue and clear out any MM_STREAM_DONE messages. We need to do
  359.     // this in order to work around a severe bug in the Creative MP3 codec (ctmp3.acm),
  360.     // which has a loop like this:
  361.     //
  362.     //    do {
  363.     //        Sleep(10);
  364.     //    } while(!PostThreadMessage(GetCurrentThreadId(), MM_STREAM_DONE, id, 0));
  365.     //
  366.     // Since we're not required to run a message pump to use ACM without a window handle,
  367.     // this causes the window message queue to fill up.
  368.  
  369.     MSG msg;
  370.     int messageCount = 0;
  371.     for(; messageCount < 500; ++messageCount) {
  372.         if (!PeekMessage(&msg, (HWND)-1, MM_STREAM_DONE, MM_STREAM_DONE, PM_REMOVE | PM_NOYIELD))
  373.             break;
  374.     }
  375.  
  376.     if (messageCount > 0) {
  377.         static bool wtf = false;
  378.  
  379.         if (!wtf) {
  380.             wtf = true;
  381.  
  382.             VDDEBUG("AudioCodec: MM_STREAM_DONE thread messages found in message queue!\n");
  383.         }
  384.  
  385.         if (messageCount >= 500) {
  386.             VDDEBUG("AudioCodec: Too many messages found!\n");
  387.         }
  388.     }
  389.  
  390.  
  391.     if (mBufferHdr.cbSrcLength || flush) {
  392.         vdprotected2(isCompression ? "compressing audio" : "decompressing audio", const char *, mDriverName, const char *, mDriverFilename) {
  393.             DWORD flags = ACM_STREAMCONVERTF_BLOCKALIGN;
  394.  
  395.             if (flush && !mBufferHdr.cbSrcLength)
  396.                 mbFlushing = true;
  397.  
  398.             if (mbFlushing)
  399.                 flags = ACM_STREAMCONVERTF_END;
  400.  
  401.             if (mbFirst)
  402.                 flags |= ACM_STREAMCONVERTF_START;
  403.  
  404.             if (MMRESULT res = acmStreamConvert(mhStream, &mBufferHdr, flags))
  405.                 throw MyError(
  406.                     isCompression
  407.                         ? "The audio codec reported an error while compressing audio data.\n\nError code: %d (%s)"
  408.                         : "The audio codec reported an error while decompressing audio data.\n\nError code: %d (%s)"
  409.                     , res
  410.                     , VDGetNameOfACMErrorW32(res));
  411.  
  412.             mbFirst = false;
  413.         }
  414.  
  415.         // If the codec didn't do anything....
  416.         if (!mBufferHdr.cbSrcLengthUsed && !mBufferHdr.cbDstLengthUsed) {
  417.             if (flush) {
  418.                 if (mbFlushing)
  419.                     mbEnded = true;
  420.                 else
  421.                     mbFlushing = true;
  422.             } else if (requireOutput) {
  423.                 // Check for a jam condition to try to trap that damned 9995 frame
  424.                 // hang problem.
  425.                 const VDWaveFormat& wfsrc = *mSrcFormat;
  426.                 const VDWaveFormat& wfdst = *mDstFormat;
  427.  
  428.                 throw MyError("The operation cannot continue as the target audio codec has jammed and is not %scompressing data.\n"
  429.                                 "Codec state for driver \"%.64s\":\n"
  430.                                 "    source buffer size: %d bytes\n"
  431.                                 "    destination buffer size: %d bytes\n"
  432.                                 "    source format: tag %04x, %dHz/%dch/%d-bit, %d bytes/sec\n"
  433.                                 "    destination format: tag %04x, %dHz/%dch/%d-bit, %d bytes/sec\n"
  434.                                 , isCompression ? "" : "de"
  435.                                 , mDriverName
  436.                                 , mBufferHdr.cbSrcLength
  437.                                 , mBufferHdr.cbDstLength
  438.                                 , wfsrc.mTag, wfsrc.mSamplingRate, wfsrc.mChannels, wfsrc.mSampleBits, wfsrc.mDataRate
  439.                                 , wfdst.mTag, wfdst.mSamplingRate, wfdst.mChannels, wfdst.mSampleBits, wfdst.mDataRate);
  440.             }
  441.         }
  442.     }
  443.  
  444.     mOutputReadPt = 0;
  445.  
  446.     // if ACM didn't use all the source data, copy the remainder down
  447.     if (mBufferHdr.cbSrcLengthUsed < mBufferHdr.cbSrcLength) {
  448.         long left = mBufferHdr.cbSrcLength - mBufferHdr.cbSrcLengthUsed;
  449.  
  450.         memmove(&mInputBuffer.front(), &mInputBuffer[mBufferHdr.cbSrcLengthUsed], left);
  451.  
  452.         mBufferHdr.cbSrcLength = left;
  453.     } else
  454.         mBufferHdr.cbSrcLength = 0;
  455.  
  456.     return mBufferHdr.cbSrcLengthUsed || mBufferHdr.cbDstLengthUsed;
  457. }
  458.  
  459. const void *VDAudioCodecW32::LockOutputBuffer(unsigned& bytes) {
  460.     bytes = mBufferHdr.cbDstLengthUsed - mOutputReadPt;
  461.     return mOutputBuffer.data() + mOutputReadPt;
  462. }
  463.  
  464. void VDAudioCodecW32::UnlockOutputBuffer(unsigned bytes) {
  465.     mOutputReadPt += bytes;
  466.     VDASSERT(mOutputReadPt <= mBufferHdr.cbDstLengthUsed);
  467. }
  468.  
  469. unsigned VDAudioCodecW32::CopyOutput(void *dst, unsigned bytes) {
  470.     bytes = std::min<unsigned>(bytes, mBufferHdr.cbDstLengthUsed - mOutputReadPt);
  471.  
  472.     if (dst)
  473.         memcpy(dst, &mOutputBuffer[mOutputReadPt], bytes);
  474.  
  475.     mOutputReadPt += bytes;
  476.     return bytes;
  477. }
  478.  
  479. ///////////////////////////////////////////////////////////////////////////
  480.  
  481. IVDAudioCodec *VDLocateAudioDecompressor(const VDWaveFormat *srcFormat, const VDWaveFormat *dstFormat, bool preferInternalCodecs, const char *pShortNameDriverHint) {
  482.     IVDAudioCodec *codec = NULL;
  483.  
  484.     if (preferInternalCodecs) {
  485.         codec = VDCreateAudioDecompressor(srcFormat, dstFormat);
  486.         if (codec)
  487.             return codec;
  488.     }
  489.  
  490.     codec = VDCreateAudioDecompressorW32(srcFormat, dstFormat, pShortNameDriverHint);
  491.     if (codec)
  492.         return codec;
  493.  
  494.     if (!preferInternalCodecs) {
  495.         codec = VDCreateAudioDecompressor(srcFormat, dstFormat);
  496.         if (codec)
  497.             return codec;
  498.     }
  499.  
  500.     throw MyError(
  501.             "No audio decompressor could be found to decompress the source audio format.\n"
  502.             "(source format tag: %04x)"
  503.             , (uint16)srcFormat->mTag
  504.         );
  505. }
  506.