/*========================================================================== * * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved. * * File: aconv2.h * Content: This module implements the CAudioConverter2 class. * * History: * Date By Reason * ==== == ====== * 08/02/99 pnewson Created * ***************************************************************************/ #include "stdafx.h" #include "aconv2.h" #include "acmutils.h" #include "dndbg.h" #include "OSInd.h" // some flags used to remember what PCM formats are supported // for converting from the source format, and converting to // the destination format #define ACONV2_MONO_08BIT_08000HZ 0x00000001 #define ACONV2_MONO_08BIT_11025HZ 0x00000002 #define ACONV2_MONO_08BIT_22050HZ 0x00000004 #define ACONV2_MONO_08BIT_44100HZ 0x00000008 #define ACONV2_MONO_16BIT_08000HZ 0x00000010 #define ACONV2_MONO_16BIT_11025HZ 0x00000020 #define ACONV2_MONO_16BIT_22050HZ 0x00000040 #define ACONV2_MONO_16BIT_44100HZ 0x00000080 CAudioConverter2::CAudioConverter2(WAVEFORMATEX *pwfxSrc, WAVEFORMATEX *pwfxDst, BOOL* pbAble) : m_iNumSteps(0) , m_apacsStepOne(NULL) , m_apbStepOneBuf(NULL) , m_dwStepOneBufSize(0) , m_apacsStepTwo(NULL) , m_apbStepTwoBuf(NULL) , m_dwStepTwoBufSize(0) , m_apacsStepThree(NULL) { WAVEFORMATEX wfx; // assume we'll be able to convert *pbAble = TRUE; // see if we can go between these two formats in one // step. if (CanACMConvert(pwfxSrc, pwfxDst)) { // a single step conversion is possible, use it m_iNumSteps = 1; m_apacsStepOne = std::auto_ptr( new CAudioConverterSingle(pwfxSrc, pwfxDst)); if (m_apacsStepOne.get() == NULL) { //// TODO(pnewson, "memory alloc failure cleanup") throw exception(); } return; } // a single step conversion wouldn't do it. // determine the mono PCM formats we can use // to convert to the destination format DWORD fDstInFmts = 0; wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = 1; wfx.cbSize = 0; // check 8bit, 8kHz, mono wfx.nSamplesPerSec = 8000; wfx.wBitsPerSample = 8; wfx.nBlockAlign = 1; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(&wfx, pwfxDst)) { fDstInFmts |= ACONV2_MONO_08BIT_08000HZ; } // check 8bit, 11kHz, mono wfx.nSamplesPerSec = 11025; wfx.wBitsPerSample = 8; wfx.nBlockAlign = 1; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(&wfx, pwfxDst)) { fDstInFmts |= ACONV2_MONO_08BIT_11025HZ; } // check 8bit, 22kHz, mono wfx.nSamplesPerSec = 22050; wfx.wBitsPerSample = 8; wfx.nBlockAlign = 1; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(&wfx, pwfxDst)) { fDstInFmts |= ACONV2_MONO_08BIT_22050HZ; } // check 8bit, 44kHz, mono wfx.nSamplesPerSec = 44100; wfx.wBitsPerSample = 8; wfx.nBlockAlign = 1; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(&wfx, pwfxDst)) { fDstInFmts |= ACONV2_MONO_08BIT_44100HZ; } // check 16bit, 8kHz, mono wfx.nSamplesPerSec = 8000; wfx.wBitsPerSample = 16; wfx.nBlockAlign = 2; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(&wfx, pwfxDst)) { fDstInFmts |= ACONV2_MONO_16BIT_08000HZ; } // check 16bit, 11kHz, mono wfx.nSamplesPerSec = 11025; wfx.wBitsPerSample = 16; wfx.nBlockAlign = 2; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(&wfx, pwfxDst)) { fDstInFmts |= ACONV2_MONO_16BIT_11025HZ; } // check 16bit, 22kHz, mono wfx.nSamplesPerSec = 22050; wfx.wBitsPerSample = 16; wfx.nBlockAlign = 2; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(&wfx, pwfxDst)) { fDstInFmts |= ACONV2_MONO_16BIT_22050HZ; } // check 16bit, 44kHz, mono wfx.nSamplesPerSec = 44100; wfx.wBitsPerSample = 16; wfx.nBlockAlign = 2; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(&wfx, pwfxDst)) { fDstInFmts |= ACONV2_MONO_16BIT_44100HZ; } // determine the mono PCM formats we can use // to convert from the source format DWORD fSrcOutFmts = 0; wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = 1; wfx.cbSize = 0; // check 8bit, 8kHz, mono wfx.nSamplesPerSec = 8000; wfx.wBitsPerSample = 8; wfx.nBlockAlign = 1; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(pwfxSrc, &wfx)) { fSrcOutFmts |= ACONV2_MONO_08BIT_08000HZ; } // check 8bit, 11kHz, mono wfx.nSamplesPerSec = 11025; wfx.wBitsPerSample = 8; wfx.nBlockAlign = 1; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(pwfxSrc, &wfx)) { fSrcOutFmts |= ACONV2_MONO_08BIT_11025HZ; } // check 8bit, 22kHz, mono wfx.nSamplesPerSec = 22050; wfx.wBitsPerSample = 8; wfx.nBlockAlign = 1; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(pwfxSrc, &wfx)) { fSrcOutFmts |= ACONV2_MONO_08BIT_22050HZ; } // check 8bit, 44kHz, mono wfx.nSamplesPerSec = 44100; wfx.wBitsPerSample = 8; wfx.nBlockAlign = 1; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(pwfxSrc, &wfx)) { fSrcOutFmts |= ACONV2_MONO_08BIT_44100HZ; } // check 16bit, 8kHz, mono wfx.nSamplesPerSec = 8000; wfx.wBitsPerSample = 16; wfx.nBlockAlign = 2; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(pwfxSrc, &wfx)) { fSrcOutFmts |= ACONV2_MONO_16BIT_08000HZ; } // check 16bit, 11kHz, mono wfx.nSamplesPerSec = 11025; wfx.wBitsPerSample = 16; wfx.nBlockAlign = 2; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(pwfxSrc, &wfx)) { fSrcOutFmts |= ACONV2_MONO_16BIT_11025HZ; } // check 16bit, 22kHz, mono wfx.nSamplesPerSec = 22050; wfx.wBitsPerSample = 16; wfx.nBlockAlign = 2; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(pwfxSrc, &wfx)) { fSrcOutFmts |= ACONV2_MONO_16BIT_22050HZ; } // check 16bit, 44kHz, mono wfx.nSamplesPerSec = 44100; wfx.wBitsPerSample = 16; wfx.nBlockAlign = 2; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; if (CanACMConvert(pwfxSrc, &wfx)) { fSrcOutFmts |= ACONV2_MONO_16BIT_44100HZ; } // if any of these formats intersect, we can do a // two step conversion DWORD fAvailFmts = fSrcOutFmts & fDstInFmts; if (fAvailFmts) { // two steps it is // Try to use the highest quality intermediate // format available. Since voice tends to be // pretty restricted in it's frequency spectrum, // the number of bits is more important than the // sampling rate, so prefer any 16 bit format to // any 8 bit format. SelectBestFmt(&wfx, fAvailFmts); // wfx now holds the intermediate format, so init the // two single step conversions. m_iNumSteps = 2; m_apacsStepOne = std::auto_ptr( new CAudioConverterSingle(pwfxSrc, &wfx)); if (m_apacsStepOne.get() == NULL) { //// TODO(pnewson, "memory alloc failure cleanup") throw exception(); } m_apacsStepTwo = std::auto_ptr( new CAudioConverterSingle(&wfx, pwfxDst)); if (m_apacsStepTwo.get() == NULL) { //// TODO(pnewson, "memory alloc failure cleanup") throw exception(); } // Now initialize our intermediate buffer. Start with // an intermediate buffer large enough to convert one // block of input data. m_apacsStepOne->MaxOutputSize(pwfxSrc->nBlockAlign, &m_dwStepOneBufSize); m_apbStepOneBuf = std::auto_ptr(new BYTE[m_dwStepOneBufSize]); return; } else { // make sure that both the source and destination format // actually can convert to PCM data! if (fSrcOutFmts == 0 && fDstInFmts == 0) { // can't do it if (pbAble != NULL) { *pbAble = FALSE; } m_iNumSteps = 0; return; } // ok, so we can do something, even though it's going to take us // three steps! m_iNumSteps = 3; // choose the highest quality intermediate formats WAVEFORMATEX wfx2; SelectBestFmt(&wfx, fSrcOutFmts); SelectBestFmt(&wfx2, fDstInFmts); // confirm that the ACM will be able to go between these // two intermedate formats!!! if (!CanACMConvert(&wfx, &wfx2)) { // can't do it if (pbAble != NULL) { *pbAble = FALSE; } m_iNumSteps = 0; return; } // initialize the first converter m_apacsStepOne = std::auto_ptr( new CAudioConverterSingle(pwfxSrc, &wfx)); if (m_apacsStepOne.get() == NULL) { //// TODO(pnewson, "memory alloc failure cleanup") throw exception(); } // initialize the second converter m_apacsStepTwo= std::auto_ptr( new CAudioConverterSingle(&wfx, &wfx2)); if (m_apacsStepTwo.get() == NULL) { //// TODO(pnewson, "memory alloc failure cleanup") throw exception(); } // initialize the third converter m_apacsStepThree = std::auto_ptr( new CAudioConverterSingle(&wfx2, pwfxDst)); if (m_apacsStepThree.get() == NULL) { //// TODO(pnewson, "memory alloc failure cleanup") throw exception(); } // Now initialize our intermediate buffers. Start with // intermediate buffers large enough to convert one // block of input data. m_apacsStepOne->MaxOutputSize(pwfxSrc->nBlockAlign, &m_dwStepOneBufSize); m_apbStepOneBuf = std::auto_ptr(new BYTE[m_dwStepOneBufSize]); if (m_apbStepOneBuf.get() == NULL) { //// TODO(pnewson, "memory alloc failure cleanup") throw exception(); } m_apacsStepTwo->MaxOutputSize(m_dwStepOneBufSize, &m_dwStepTwoBufSize); m_apbStepTwoBuf = std::auto_ptr(new BYTE[m_dwStepTwoBufSize]); if (m_apbStepTwoBuf.get() == NULL) { //// TODO(pnewson, "memory alloc failure cleanup") throw exception(); } return; } return; } CAudioConverter2::~CAudioConverter2() { // auto ptrs will clean up automatically return; } void CAudioConverter2::MaxOutputSize(DWORD dwInputSize, DWORD* pdwOutputSize) { DWORD dwStepOne; DWORD dwStepTwo; switch (m_iNumSteps) { case 1: m_apacsStepOne->MaxOutputSize(dwInputSize, pdwOutputSize); break; case 2: m_apacsStepOne->MaxOutputSize(dwInputSize, &dwStepOne); m_apacsStepTwo->MaxOutputSize(dwStepOne, pdwOutputSize); break; case 3: m_apacsStepOne->MaxOutputSize(dwInputSize, &dwStepOne); m_apacsStepTwo->MaxOutputSize(dwStepOne, &dwStepTwo); m_apacsStepThree->MaxOutputSize(dwStepTwo, pdwOutputSize); break; default: // this catches the case where the converter was not // able to do the conversion. //// TODO(pnewson, "revisit this error case") DNASSERT(TRUE); } return; } void CAudioConverter2::Convert( BYTE* pbInputBuf, DWORD dwInputSize, BYTE* pbOutputBuf, DWORD dwOutputBufSize, DWORD* pdwActualSize) { DWORD dwStepOneSize; DWORD dwStepOneOutputSize; DWORD dwStepTwoSize; DWORD dwStepTwoOutputSize; switch (m_iNumSteps) { case 1: // one step, so just do it m_apacsStepOne->Convert(pbInputBuf, dwInputSize, pbOutputBuf, dwOutputBufSize, pdwActualSize); break; case 2: // two steps, so we need to convert it to our internal // first step buffer first, and then to the caller's // output buffer // check the buffer size m_apacsStepOne->MaxOutputSize(dwInputSize, &dwStepOneSize); if (dwStepOneSize > m_dwStepOneBufSize) { // need a bigger buffer std::auto_ptr apbNewBuf(new BYTE[dwStepOneSize]); if (apbNewBuf.get() == NULL) { //// TODO(pnewson, "memory alloc failure cleanup") throw exception(); } // note - auto_ptr deletes old buffer m_apbStepOneBuf = apbNewBuf; m_dwStepOneBufSize = dwStepOneSize; } // we have a large enough temp buffer, so do the first step // conversion m_apacsStepOne->Convert(pbInputBuf, dwInputSize, m_apbStepOneBuf.get(), m_dwStepOneBufSize, &dwStepOneOutputSize); // now do the second step conversion, into the caller's buffer m_apacsStepTwo->Convert(m_apbStepOneBuf.get(), dwStepOneOutputSize, pbOutputBuf, dwOutputBufSize, pdwActualSize); break; case 3: // three steps, so we need to convert it to our internal // first step buffer first, and then to seconds step buffer, // and then finally into to the caller's output buffer // check the first step buffer size m_apacsStepOne->MaxOutputSize(dwInputSize, &dwStepOneSize); if (dwStepOneSize > m_dwStepOneBufSize) { // need a bigger buffer std::auto_ptr apbNewBuf(new BYTE[dwStepOneSize]); if (apbNewBuf.get() == NULL) { //// TODO(pnewson, "memory alloc failure cleanup") throw exception(); } // note - auto_ptr deletes old buffer m_apbStepOneBuf = apbNewBuf; m_dwStepOneBufSize = dwStepOneSize; } // we have a large enough temp buffer, so do the first step // conversion m_apacsStepOne->Convert(pbInputBuf, dwInputSize, m_apbStepOneBuf.get(), m_dwStepOneBufSize, &dwStepOneOutputSize); // check the second step buffer size m_apacsStepTwo->MaxOutputSize(dwStepOneOutputSize, &dwStepTwoSize); if (dwStepTwoSize > m_dwStepTwoBufSize) { // need a bigger buffer std::auto_ptr apbNewBuf(new BYTE[dwStepTwoSize]); if (apbNewBuf.get() == NULL) { //// TODO(pnewson, "memory alloc failure cleanup") throw exception(); } // note - auto_ptr deletes old buffer m_apbStepTwoBuf = apbNewBuf; m_dwStepTwoBufSize = dwStepTwoSize; } // we have a large enough temp buffer, so do the second step // conversion m_apacsStepTwo->Convert(m_apbStepOneBuf.get(), dwStepOneOutputSize, m_apbStepTwoBuf.get(), m_dwStepTwoBufSize, &dwStepTwoOutputSize); // now do the third step conversion, into the caller's buffer m_apacsStepThree->Convert(m_apbStepTwoBuf.get(), dwStepTwoOutputSize, pbOutputBuf, dwOutputBufSize, pdwActualSize); break; default: // this catches the case where the converter was not // able to do the conversion. //// TODO(pnewson, "revisit this error case") DNASSERT(TRUE); } return; } void CAudioConverter2::Flush() { switch(m_iNumSteps) { case 3: m_apacsStepThree->Flush(); // Note - falling through! case 2: m_apacsStepTwo->Flush(); // Note - falling through! case 1: m_apacsStepOne->Flush(); break; default: // this catches the case where the converter was not // able to do the conversion. //// TODO(pnewson, "revisit this error case") DNASSERT(TRUE); } } bool CAudioConverter2::CanACMConvert(WAVEFORMATEX* pwfxSrc, WAVEFORMATEX* pwfxDst) { MMRESULT mmr; mmr = acmStreamOpen(NULL, NULL, pwfxSrc, pwfxDst, NULL, 0, 0, ACM_STREAMOPENF_QUERY); if (mmr == 0) { return true; } if (mmr != ACMERR_NOTPOSSIBLE) { // this was an unexpected error, so throw an exception. ACMCHECK(mmr); } return false; } void CAudioConverter2::SelectBestFmt(WAVEFORMATEX* pwfx, DWORD fAvailFmts) { pwfx->wFormatTag = WAVE_FORMAT_PCM; pwfx->nChannels = 1; pwfx->cbSize = 0; if (fAvailFmts & ACONV2_MONO_16BIT_44100HZ) { pwfx->nSamplesPerSec = 44100; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = 2; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; } else if (fAvailFmts & ACONV2_MONO_16BIT_22050HZ) { pwfx->nSamplesPerSec = 22050; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = 2; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; } else if (fAvailFmts & ACONV2_MONO_16BIT_11025HZ) { pwfx->nSamplesPerSec = 11025; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = 2; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; } else if (fAvailFmts & ACONV2_MONO_16BIT_08000HZ) { pwfx->nSamplesPerSec = 8000; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = 2; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; } else if (fAvailFmts & ACONV2_MONO_08BIT_44100HZ) { pwfx->nSamplesPerSec = 44100; pwfx->wBitsPerSample = 8; pwfx->nBlockAlign = 1; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; } else if (fAvailFmts & ACONV2_MONO_08BIT_22050HZ) { pwfx->nSamplesPerSec = 22050; pwfx->wBitsPerSample = 8; pwfx->nBlockAlign = 1; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; } else if (fAvailFmts & ACONV2_MONO_08BIT_11025HZ) { pwfx->nSamplesPerSec = 11025; pwfx->wBitsPerSample = 8; pwfx->nBlockAlign = 1; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; } else if (fAvailFmts & ACONV2_MONO_08BIT_08000HZ) { pwfx->nSamplesPerSec = 8000; pwfx->wBitsPerSample = 8; pwfx->nBlockAlign = 1; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; } else { // we should never get here. one of the // formats above was supposed to work! DNASSERT(FALSE); } }