2025-04-27 07:49:33 -04:00

635 lines
17 KiB
C++

/*==========================================================================
*
* 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<CAudioConverterSingle>(
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<CAudioConverterSingle>(
new CAudioConverterSingle(pwfxSrc, &wfx));
if (m_apacsStepOne.get() == NULL)
{
//// TODO(pnewson, "memory alloc failure cleanup")
throw exception();
}
m_apacsStepTwo = std::auto_ptr<CAudioConverterSingle>(
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<BYTE>(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<CAudioConverterSingle>(
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<CAudioConverterSingle>(
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<CAudioConverterSingle>(
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<BYTE>(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<BYTE>(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<BYTE> 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<BYTE> 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<BYTE> 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);
}
}