📄 acm.c
字号:
// NOTE! It is _assumed_ that the format is a valid IMA-ADPCM format
// and that the following fields in the format structure are valid:
//
// nChannels
// nSamplesPerSec
//
// Arguments:
// LPWAVEFORMATEX pwfx: Pointer to format header.
//
// Return (UINT):
// The return value is the block alignment that should be placed in
// the pwfx->nBlockAlign field.
//
//--------------------------------------------------------------------------;
WORD FNLOCAL IMAAlgorithBlockAlign
(
LPWAVEFORMATEX pwfx
)
{
UINT uBlockAlign;
UINT uChannelShift;
//
// The data must be an integral number of DWORDs for mono, an even
// number of DWORDs for stereo.
//
uChannelShift = pwfx->nChannels >> 1;
uBlockAlign = 256 << uChannelShift;
//
// choose a block alignment that makes sense for the sample rate
// that the original PCM data is. basically, this needs to be
// some reasonable number to allow efficient streaming, etc.
//
// don't let block alignment get too small...
//
if (pwfx->nSamplesPerSec > 11025)
{
uBlockAlign *= (UINT)(pwfx->nSamplesPerSec / 11000);
}
//
// Just make sure that the alignment is valid.
//
ASSERT( 0 == uBlockAlign % (sizeof(DWORD)*pwfx->nChannels) );
return (WORD)(uBlockAlign);
} // IMAAlgorithBlockAlign()
//--------------------------------------------------------------------------;
//
// WORD IMAAlgorithSamplesPerBlock
//
// Description:
// This function computes the Samples Per Block that should be used
// given the WAVEFORMATEX structure.
//
// NOTE! It is _assumed_ that the format is a valid IMA-ADPCM format
// and that the following fields in the format structure are valid:
//
// nChannels = must be 1 or 2!
// nSamplesPerSec
// nBlockAlign
//
// Arguments:
// LPWAVEFORMATEX pwfx: Pointer to format header.
//
// Return (DWORD):
// The return value is the average bytes per second that should be
// placed in the pwfx->nAvgBytesPerSec field.
//
//--------------------------------------------------------------------------;
WORD FNLOCAL IMAAlgorithSamplesPerBlock
(
LPWAVEFORMATEX pwfx
)
{
UINT uSamplesPerBlock;
UINT uChannelShift;
UINT uHeaderBytes;
UINT uBitsPerSample;
//
//
//
uChannelShift = pwfx->nChannels >> 1;
uHeaderBytes = 4 << uChannelShift;
uBitsPerSample = IMAALGORITH_BITS_PER_SAMPLE << uChannelShift;
//
// compute the 'samples per block' that will be in the encoded
// ADPCM data blocks. this is determined by subtracting out the
// 'other info' contained in each block--a block is composed of
// a header followed by the encoded data.
//
// the block header is composed of the following data:
// 2 bytes (16 bit) sample per channel
// 1 byte for step table index per channel
// 1 byte padding per channel (dword align)
//
// this gives us (4 * uChannels) bytes of header information that
// contains our first full sample (so we add one below).
//
uSamplesPerBlock = (pwfx->nBlockAlign - uHeaderBytes) * 8;
uSamplesPerBlock /= uBitsPerSample;
uSamplesPerBlock += 1;
return (WORD)(uSamplesPerBlock);
} // IMAAlgorithSamplesPerBlock()
//--------------------------------------------------------------------------;
//
// UINT IMAAlgorithAvgBytesPerSec
//
// Description:
// This function computes the Average Bytes Per Second that should
// be used given the WAVEFORMATEX structure.
//
// NOTE! It is _assumed_ that the format is a valid IMA-ADPCM format
// and that the following fields in the format structure are valid:
//
// nChannels = must be 1 or 2!
// nSamplesPerSec
// nBlockAlign
//
// Arguments:
// LPWAVEFORMATEX pwfx: Pointer to format header.
//
// Return (DWORD):
// The return value is the average bytes per second that should be
// placed in the pwfx->nAvgBytesPerSec field.
//
//--------------------------------------------------------------------------;
DWORD FNLOCAL IMAAlgorithAvgBytesPerSec
(
LPWAVEFORMATEX pwfx
)
{
DWORD dwAvgBytesPerSec;
UINT uSamplesPerBlock;
//
//
//
uSamplesPerBlock = IMAAlgorithSamplesPerBlock(pwfx);
//
// compute bytes per second including header bytes
//
dwAvgBytesPerSec = (pwfx->nSamplesPerSec * pwfx->nBlockAlign) /
uSamplesPerBlock;
return (dwAvgBytesPerSec);
} // IMAAlgorithAvgBytesPerSec()
//==========================================================================;
//
//
//
//
//==========================================================================;
//--------------------------------------------------------------------------;
//
// LRESULT acmdDriverOpen
//
// Description:
// This function is used to handle the DRV_OPEN message for the ACM
// driver. The driver is 'opened' for many reasons with the most common
// being in preperation for conversion work. It is very important that
// the driver be able to correctly handle multiple open driver
// instances.
//
// Read the comments for this function carefully!
//
// Note that multiple _streams_ can (and will) be opened on a single
// open _driver instance_. Do not store/create instance data that must
// be unique for each stream in this function. See the acmdStreamOpen
// function for information on conversion streams.
//
// Arguments:
// HDRVR hdrvr: Driver handle that will be returned to caller of the
// OpenDriver function. Normally, this will be the ACM--but this is
// not guaranteed. For example, if an ACM driver is implemented within
// a waveform driver, then the driver will be opened by both MMSYSTEM
// and the ACM.
//
// LPACMDRVOPENDESC paod: Open description defining how the ACM driver
// is being opened. This argument may be NULL--see the comments below
// for more information.
//
// Return (LRESULT):
// The return value is non-zero if the open is successful. A zero
// return signifies that the driver cannot be opened.
//
//--------------------------------------------------------------------------;
LRESULT FNLOCAL acmdDriverOpen
(
HDRVR hdrvr,
LPACMDRVOPENDESC paod
)
{
PDRIVERINSTANCE pdi;
//
// the [optional] open description that is passed to this driver can
// be from multiple 'managers.' for example, AVI looks for installable
// drivers that are tagged with 'vidc' and 'vcap'. we need to verify
// that we are being opened as an Audio Compression Manager driver.
//
// if paod is NULL, then the driver is being opened for some purpose
// other than converting (that is, there will be no stream open
// requests for this instance of being opened). the most common case
// of this is the Control Panel's Drivers option checking for Configur
// support (DRV_[QUERY]CONFIGURURE).
//
// we want to succeed this open, but be able to know that this
// open instance is bogus for creating streams. for this purpose we
// leave most of the members of our instance structure that we
// allocate below as zero...
//
if (NULL != paod)
{
//
// refuse to open if we are not being opened as an ACM driver.
// note that we do NOT modify the value of paod->dwError in this
// case.
//
if (ACMDRIVERDETAILS_FCCTYPE_AUDIOACM != paod->fccType)
{
return (0L);
}
}
//
// we are being opened as an installable driver--we can allocate some
// instance data to be returned in dwId argument of the DriverProc;
// or simply return non-zero to succeed the open.
//
// this driver allocates a small instance structure. note that we
// rely on allocating the memory as zero-initialized!
//
pdi = (PDRIVERINSTANCE)LocalAlloc(LPTR, sizeof(*pdi));
if (NULL == pdi)
{
//
// if this open attempt was as an ACM driver, then return the
// reason we are failing in the open description structure..
//
if (NULL != paod)
{
paod->dwError = MMSYSERR_NOMEM;
}
//
// fail to open
//
return (0L);
}
//
// fill in our instance structure... note that this instance data
// can be anything that the ACM driver wishes to maintain the
// open driver instance. this data should not contain any information
// that must be maintained per open stream since multiple streams
// can be opened on a single driver instance.
//
// also note that we do _not_ check the version of the ACM opening
// us (paod->dwVersion) to see if it is at least new enough to work
// with this driver (for example, if this driver required Version 3.0
// of the ACM and a Version 2.0 installation tried to open us). the
// reason we do not fail is to allow the ACM to get the driver details
// which contains the version of the ACM that is _required_ by this
// driver. the ACM will examine that value (in padd->vdwACM) and
// do the right thing for this driver... like not load it and inform
// the user of the problem.
//
pdi->hdrvr = hdrvr;
pdi->hinst = GetDriverModuleHandle(hdrvr); // Module handle.
if (NULL != paod)
{
pdi->fnDriverProc = NULL;
pdi->fccType = paod->fccType;
pdi->vdwACM = paod->dwVersion;
pdi->fdwOpen = paod->dwFlags;
paod->dwError = MMSYSERR_NOERROR;
}
#ifdef IMAALGORITH_USECONFIGUR
//
// Get Configur info for this driver. If we're not passed an
// an ACMDRVOPENDESC structure then we'll assume we are being
// opened for Configururation and will put off getting the Configur
// info until we receive the DRV_CONFIGURURE message. Otherwise we
// get the Configur info now using the alias passed through the
// ACMDRVOPENDESC structure.
//
pdi->hkey = NULL; // This is important!
if (NULL != paod)
{
#if defined(WIN32) && !defined(UNICODE)
//
// We must translate the UNICODE alias name to an ANSI version
// that we can use.
//
LPSTR lpstr;
int iLen;
//
// Calculate required length without calling UNICODE APIs or CRT.
//
iLen = WideCharToMultiByte( GetACP(), 0, paod->pszAliasName,-1,
NULL, 0, NULL, NULL );
lpstr = (LPSTR)GlobalAllocPtr( GPTR, iLen );
if (NULL != lpstr)
{
WideCharToMultiByte( GetACP(), 0, paod->pszAliasName, iLen,
lpstr, iLen, NULL, NULL );
}
acmdDriverConfigurInit(pdi, lpstr); // Note: OK to pass lpstr==NULL
if (NULL != lpstr)
{
GlobalFreePtr( lpstr );
}
#else
acmdDriverConfigurInit(pdi, paod->pszAliasName);
#endif // WIN32 && !UNICODE
}
#else
//
// Actually, fdwConfigur is not used - there is no Configururation data.
//
pdi->fdwConfigur = 0L;
#endif // IMAALGORITH_USECONFIGUR
//
// non-zero return is success for DRV_OPEN
//
return ((LRESULT)pdi);
} // acmdDriverOpen()
//--------------------------------------------------------------------------;
//
// LRESULT acmdDriverClose
//
// Description:
// This function handles the DRV_CLOSE message for the ACM driver. The
// driver receives a DRV_CLOSE message for each succeeded DRV_OPEN
// message (see acmdDriverOpen). The driver will only receive a close
// message for _successful_ opens.
//
// Arguments:
// PDRIVERINSTANCE pdi: Pointer to private ACM driver instance structure.
// This structure is [optionally] allocated during the DRV_OPEN message
// which is handled by the acmdDriverOpen function.
//
// Return (LRESULT):
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -