You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2556 lines
78 KiB
2556 lines
78 KiB
//==========================================================================;
|
|
//
|
|
// codec.c
|
|
//
|
|
// Copyright (c) 1992-1999 Microsoft Corporation
|
|
//
|
|
// Description:
|
|
//
|
|
//
|
|
// History:
|
|
//
|
|
//==========================================================================;
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <mmsystem.h>
|
|
#include <mmddk.h>
|
|
#include <mmreg.h>
|
|
#include <ctype.h>
|
|
#include <msacm.h>
|
|
#include <msacmdrv.h>
|
|
#include "codec.h"
|
|
#include "msadpcm.h"
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
#define SIZEOF_ARRAY(ar) (sizeof(ar)/sizeof((ar)[0]))
|
|
|
|
|
|
const UINT gauFormatTagIndexToTag[] =
|
|
{
|
|
WAVE_FORMAT_PCM,
|
|
WAVE_FORMAT_ADPCM
|
|
};
|
|
|
|
#define CODEC_MAX_FORMAT_TAGS SIZEOF_ARRAY(gauFormatTagIndexToTag)
|
|
#define CODEC_MAX_FILTER_TAGS 0
|
|
|
|
|
|
//
|
|
// array of sample rates supported
|
|
//
|
|
//
|
|
const UINT gauFormatIndexToSampleRate[] =
|
|
{
|
|
8000,
|
|
11025,
|
|
22050,
|
|
44100
|
|
};
|
|
|
|
#define CODEC_MAX_SAMPLE_RATES SIZEOF_ARRAY(gauFormatIndexToSampleRate)
|
|
|
|
|
|
//
|
|
// array of bits per sample supported
|
|
//
|
|
//
|
|
const UINT gauFormatIndexToBitsPerSample[] =
|
|
{
|
|
8,
|
|
16
|
|
};
|
|
|
|
#define CODEC_MAX_BITSPERSAMPLE_PCM SIZEOF_ARRAY(gauFormatIndexToBitsPerSample)
|
|
#define CODEC_MAX_BITSPERSAMPLE_ADPCM 1
|
|
|
|
|
|
#define CODEC_MAX_CHANNELS MSADPCM_MAX_CHANNELS
|
|
|
|
|
|
//
|
|
// number of formats we enumerate per channels is number of sample rates
|
|
// times number of channels times number of
|
|
// (bits per sample) types.
|
|
//
|
|
#define CODEC_MAX_FORMATS_PCM (CODEC_MAX_SAMPLE_RATES * \
|
|
CODEC_MAX_CHANNELS * \
|
|
CODEC_MAX_BITSPERSAMPLE_PCM)
|
|
|
|
#define CODEC_MAX_FORMATS_ADPCM (CODEC_MAX_SAMPLE_RATES * \
|
|
CODEC_MAX_CHANNELS * \
|
|
CODEC_MAX_BITSPERSAMPLE_ADPCM)
|
|
|
|
|
|
//==========================================================================;
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================;
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// int LoadStringCodec
|
|
//
|
|
// Description:
|
|
// This function should be used by all codecs to load resource strings
|
|
// which will be passed back to the ACM. It works correctly for all
|
|
// platforms, as follows:
|
|
//
|
|
// Win16: Compiled to LoadString to load ANSI strings.
|
|
//
|
|
// Win32: The 32-bit ACM always expects Unicode strings. Therefore,
|
|
// when UNICODE is defined, this function is compiled to
|
|
// LoadStringW to load a Unicode string. When UNICODE is
|
|
// not defined, this function loads an ANSI string, converts
|
|
// it to Unicode, and returns the Unicode string to the
|
|
// codec.
|
|
//
|
|
// Note that you may use LoadString for other strings (strings which
|
|
// will not be passed back to the ACM), because these strings will
|
|
// always be consistent with the definition of UNICODE.
|
|
//
|
|
// Arguments:
|
|
// Same as LoadString, except that it expects an LPSTR for Win16 and a
|
|
// LPWSTR for Win32.
|
|
//
|
|
// Return (int):
|
|
// Same as LoadString.
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
#ifndef WIN32
|
|
#define LoadStringCodec LoadString
|
|
#else
|
|
|
|
#ifdef UNICODE
|
|
#define LoadStringCodec LoadStringW
|
|
#else
|
|
|
|
int FNGLOBAL LoadStringCodec
|
|
(
|
|
HINSTANCE hinst,
|
|
UINT uID,
|
|
LPWSTR lpwstr,
|
|
int cch)
|
|
{
|
|
LPSTR lpstr;
|
|
int iReturn;
|
|
|
|
lpstr = (LPSTR)GlobalAlloc(GPTR, cch);
|
|
if (NULL == lpstr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
iReturn = LoadStringA(hinst, uID, lpstr, cch);
|
|
if (0 == iReturn)
|
|
{
|
|
if (0 != cch)
|
|
{
|
|
lpwstr[0] = '\0';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MultiByteToWideChar( GetACP(), 0, lpstr, cch, lpwstr, cch );
|
|
}
|
|
|
|
GlobalFree((HGLOBAL)lpstr);
|
|
|
|
return iReturn;
|
|
}
|
|
|
|
#endif // UNICODE
|
|
#endif // WIN32
|
|
|
|
|
|
//==========================================================================;
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================;
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// BOOL pcmIsValidFormat
|
|
//
|
|
// Description:
|
|
// This function verifies that a wave format header is a valid PCM
|
|
// header that our PCM converter can deal with.
|
|
//
|
|
// Arguments:
|
|
// LPWAVEFORMATEX pwfx: Pointer to format header to verify.
|
|
//
|
|
// Return (BOOL):
|
|
// The return value is non-zero if the format header looks valid. A
|
|
// zero return means the header is not valid.
|
|
//
|
|
// History:
|
|
// 11/21/92 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
BOOL FNLOCAL pcmIsValidFormat
|
|
(
|
|
LPWAVEFORMATEX pwfx
|
|
)
|
|
{
|
|
UINT uBlockAlign;
|
|
|
|
if (!pwfx)
|
|
return (FALSE);
|
|
|
|
if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
|
|
return (FALSE);
|
|
|
|
if ((pwfx->wBitsPerSample != 8) && (pwfx->wBitsPerSample != 16))
|
|
return (FALSE);
|
|
|
|
if ((pwfx->nChannels < 1) || (pwfx->nChannels > MSADPCM_MAX_CHANNELS))
|
|
return (FALSE);
|
|
|
|
//
|
|
// now verify that the block alignment is correct..
|
|
//
|
|
uBlockAlign = PCM_BLOCKALIGNMENT(pwfx);
|
|
if (uBlockAlign != (UINT)pwfx->nBlockAlign)
|
|
return (FALSE);
|
|
|
|
//
|
|
// finally, verify that avg bytes per second is correct
|
|
//
|
|
if ((pwfx->nSamplesPerSec * uBlockAlign) != pwfx->nAvgBytesPerSec)
|
|
return (FALSE);
|
|
|
|
return (TRUE);
|
|
} // pcmIsValidFormat()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// WORD adpcmBlockAlign
|
|
//
|
|
// Description:
|
|
// This function computes the standard block alignment that should
|
|
// be used given the WAVEFORMATEX structure.
|
|
//
|
|
// NOTE! It is _assumed_ that the format is a valid MS-ADPCM format
|
|
// and that the following fields in the format structure are valid:
|
|
//
|
|
// nChannels
|
|
// nSamplesPerSec
|
|
//
|
|
// Arguments:
|
|
// LPWAVEFORMATEX pwfx:
|
|
//
|
|
// Return (WORD):
|
|
// The return value is the block alignment that should be placed in
|
|
// the pwfx->nBlockAlign field.
|
|
//
|
|
// History:
|
|
// 06/13/93 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
WORD FNLOCAL adpcmBlockAlign
|
|
(
|
|
LPWAVEFORMATEX pwfx
|
|
)
|
|
{
|
|
UINT uBlockAlign;
|
|
UINT uChannelShift;
|
|
|
|
//
|
|
//
|
|
//
|
|
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);
|
|
}
|
|
|
|
return (WORD)(uBlockAlign);
|
|
} // adpcmBlockAlign()
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// WORD adpcmSamplesPerBlock
|
|
//
|
|
// 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 MS-ADPCM format
|
|
// and that the following fields in the format structure are valid:
|
|
//
|
|
// nChannels = must be 1 or 2!
|
|
// nSamplesPerSec
|
|
// nBlockAlign
|
|
//
|
|
// Arguments:
|
|
// LPWAVEFORMATEX pwfx:
|
|
//
|
|
// Return (DWORD):
|
|
// The return value is the average bytes per second that should be
|
|
// placed in the pwfx->nAvgBytesPerSec field.
|
|
//
|
|
// History:
|
|
// 06/13/93 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
WORD FNLOCAL adpcmSamplesPerBlock
|
|
(
|
|
LPWAVEFORMATEX pwfx
|
|
)
|
|
{
|
|
UINT uSamplesPerBlock;
|
|
UINT uChannelShift;
|
|
UINT uHeaderBytes;
|
|
UINT uBitsPerSample;
|
|
|
|
//
|
|
//
|
|
//
|
|
uChannelShift = pwfx->nChannels >> 1;
|
|
uHeaderBytes = 7 << uChannelShift;
|
|
uBitsPerSample = MSADPCM_BITS_PER_SAMPLE << uChannelShift;
|
|
|
|
//
|
|
//
|
|
//
|
|
uSamplesPerBlock = (pwfx->nBlockAlign - uHeaderBytes) * 8;
|
|
uSamplesPerBlock /= uBitsPerSample;
|
|
uSamplesPerBlock += 2;
|
|
|
|
return (WORD)(uSamplesPerBlock);
|
|
} // adpcmSamplesPerBlock()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// UINT adpcmAvgBytesPerSec
|
|
//
|
|
// 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 MS-ADPCM format
|
|
// and that the following fields in the format structure are valid:
|
|
//
|
|
// nChannels = must be 1 or 2!
|
|
// nSamplesPerSec
|
|
// nBlockAlign
|
|
//
|
|
// Arguments:
|
|
// LPWAVEFORMATEX pwfx:
|
|
//
|
|
// Return (DWORD):
|
|
// The return value is the average bytes per second that should be
|
|
// placed in the pwfx->nAvgBytesPerSec field.
|
|
//
|
|
// History:
|
|
// 06/13/93 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
DWORD FNLOCAL adpcmAvgBytesPerSec
|
|
(
|
|
LPWAVEFORMATEX pwfx
|
|
)
|
|
{
|
|
DWORD dwAvgBytesPerSec;
|
|
UINT uSamplesPerBlock;
|
|
|
|
//
|
|
//
|
|
//
|
|
uSamplesPerBlock = adpcmSamplesPerBlock(pwfx);
|
|
|
|
|
|
//
|
|
// compute bytes per second including header bytes
|
|
//
|
|
dwAvgBytesPerSec = (pwfx->nSamplesPerSec * pwfx->nBlockAlign) /
|
|
uSamplesPerBlock;
|
|
return (dwAvgBytesPerSec);
|
|
} // adpcmAvgBytesPerSec()
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// BOOL adpcmIsValidFormat
|
|
//
|
|
// Description:
|
|
//
|
|
//
|
|
// Arguments:
|
|
//
|
|
//
|
|
// Return (BOOL FNLOCAL):
|
|
//
|
|
//
|
|
// History:
|
|
// 1/26/93 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
BOOL FNLOCAL adpcmIsValidFormat
|
|
(
|
|
LPWAVEFORMATEX pwfx
|
|
)
|
|
{
|
|
LPADPCMWAVEFORMAT pwfADPCM = (LPADPCMWAVEFORMAT)pwfx;
|
|
|
|
if (!pwfx)
|
|
return (FALSE);
|
|
|
|
if (pwfx->wFormatTag != WAVE_FORMAT_ADPCM)
|
|
return (FALSE);
|
|
|
|
//
|
|
// check wBitsPerSample
|
|
//
|
|
if (pwfx->wBitsPerSample != MSADPCM_BITS_PER_SAMPLE)
|
|
return (FALSE);
|
|
|
|
//
|
|
// check channels
|
|
//
|
|
if ((pwfx->nChannels < 1) || (pwfx->nChannels > MSADPCM_MAX_CHANNELS))
|
|
return (FALSE);
|
|
|
|
//
|
|
// verify that there is at least enough space specified in cbSize
|
|
// for the extra info for the ADPCM header...
|
|
//
|
|
if (pwfx->cbSize < MSADPCM_WFX_EXTRA_BYTES)
|
|
return (FALSE);
|
|
|
|
//
|
|
// Verifying nBlockAlign and wSamplesPerBlock are consistent.
|
|
//
|
|
if ( (pwfADPCM->wSamplesPerBlock != adpcmSamplesPerBlock(pwfx)) )
|
|
return FALSE;
|
|
|
|
|
|
|
|
return (TRUE);
|
|
} // adpcmIsValidFormat()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// BOOL adpcmIsMagicFormat
|
|
//
|
|
// Description:
|
|
//
|
|
//
|
|
// Arguments:
|
|
//
|
|
//
|
|
// Return (BOOL FNLOCAL):
|
|
//
|
|
//
|
|
// History:
|
|
// 1/27/93 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
BOOL FNLOCAL adpcmIsMagicFormat
|
|
(
|
|
LPADPCMWAVEFORMAT pwfADPCM
|
|
)
|
|
{
|
|
UINT u;
|
|
|
|
//
|
|
// verify that there is at least enough space specified in cbSize
|
|
// for the extra info for the ADPCM header...
|
|
//
|
|
if (pwfADPCM->wfx.cbSize < MSADPCM_WFX_EXTRA_BYTES)
|
|
return (FALSE);
|
|
|
|
//
|
|
// check coef's to see if it is Microsoft's standard ADPCM
|
|
//
|
|
if (pwfADPCM->wNumCoef != MSADPCM_MAX_COEFFICIENTS)
|
|
return (FALSE);
|
|
|
|
for (u = 0; u < MSADPCM_MAX_COEFFICIENTS; u++)
|
|
{
|
|
if (pwfADPCM->aCoef[u].iCoef1 != gaiCoef1[u])
|
|
return (FALSE);
|
|
|
|
if (pwfADPCM->aCoef[u].iCoef2 != gaiCoef2[u])
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
} // adpcmIsMagicFormat()
|
|
|
|
|
|
//==========================================================================;
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================;
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// BOOL adpcmCopyCoefficients
|
|
//
|
|
// Description:
|
|
//
|
|
//
|
|
// Arguments:
|
|
// LPADPCMWAVEFORMAT pwfadpcm:
|
|
//
|
|
// Return (BOOL):
|
|
//
|
|
// History:
|
|
// 06/13/93 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
BOOL FNLOCAL adpcmCopyCoefficients
|
|
(
|
|
LPADPCMWAVEFORMAT pwfadpcm
|
|
)
|
|
{
|
|
UINT u;
|
|
|
|
pwfadpcm->wNumCoef = MSADPCM_MAX_COEFFICIENTS;
|
|
|
|
for (u = 0; u < MSADPCM_MAX_COEFFICIENTS; u++)
|
|
{
|
|
pwfadpcm->aCoef[u].iCoef1 = (short)gaiCoef1[u];
|
|
pwfadpcm->aCoef[u].iCoef2 = (short)gaiCoef2[u];
|
|
}
|
|
|
|
return (TRUE);
|
|
} // adpcmCopyCoefficients()
|
|
|
|
|
|
//==========================================================================;
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================;
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// 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
|
|
)
|
|
{
|
|
PCODECINST pci;
|
|
|
|
//
|
|
// 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 config
|
|
// support (DRV_[QUERY]CONFIGURE).
|
|
//
|
|
// 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_AUDIOCODEC != 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!
|
|
//
|
|
pci = (PCODECINST)LocalAlloc(LPTR, sizeof(*pci));
|
|
if (NULL == pci)
|
|
{
|
|
//
|
|
// 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.
|
|
//
|
|
pci->hdrvr = hdrvr;
|
|
pci->hinst = GetDriverModuleHandle(hdrvr); // Module handle.
|
|
|
|
if (NULL != paod)
|
|
{
|
|
pci->DriverProc = NULL;
|
|
pci->fccType = paod->fccType;
|
|
pci->vdwACM = paod->dwVersion;
|
|
pci->dwFlags = paod->dwFlags;
|
|
|
|
paod->dwError = MMSYSERR_NOERROR;
|
|
}
|
|
|
|
|
|
//
|
|
// non-zero return is success for DRV_OPEN
|
|
//
|
|
return ((LRESULT)pci);
|
|
} // acmdDriverOpen()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdDriverClose
|
|
//
|
|
// Description:
|
|
// This function handles the DRV_CLOSE message for the codec. The
|
|
// codec receives a DRV_CLOSE message for each succeeded DRV_OPEN
|
|
// message (see acmdDriverOpen).
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci: Pointer to private codec instance structure.
|
|
//
|
|
// Return (LRESULT):
|
|
// The return value is non-zero if the open instance can be closed.
|
|
// A zero return signifies that the codec instance could not be
|
|
// closed.
|
|
//
|
|
// NOTE! It is _strongly_ recommended that the codec never fail to
|
|
// close.
|
|
//
|
|
// History:
|
|
// 11/28/92 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdDriverClose
|
|
(
|
|
PCODECINST pci
|
|
)
|
|
{
|
|
//
|
|
// check to see if we allocated instance data. if we did not, then
|
|
// immediately succeed.
|
|
//
|
|
if (pci != NULL)
|
|
{
|
|
//
|
|
// close down the conversion instance. this codec simply needs
|
|
// to free the instance data structure...
|
|
//
|
|
LocalFree((HLOCAL)pci);
|
|
}
|
|
|
|
//
|
|
// non-zero return is success for DRV_CLOSE
|
|
//
|
|
return (1L);
|
|
} // acmdDriverClose()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdDriverConfigure
|
|
//
|
|
// Description:
|
|
// This function is called to handle the DRV_[QUERY]CONFIGURE messages.
|
|
// These messages are for 'hardware configuration' support of the
|
|
// codec. That is, a dialog should be displayed to configure ports,
|
|
// IRQ's, memory mappings, etc if it needs to.
|
|
//
|
|
// The most common way that these messages are generated under Win 3.1
|
|
// and NT Product 1 is from the Control Panel's Drivers option. Other
|
|
// sources may generate these messages in future versions of Windows.
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci: Pointer to private codec instance structure.
|
|
//
|
|
// HWND hwnd: Handle to parent window to use when displaying hardware
|
|
// configuration dialog box. A codec is _required_ to display a modal
|
|
// dialog box using this hwnd argument as the parent. This argument
|
|
// may be (HWND)-1 which tells the codec that it is only being
|
|
// queried for configuration support.
|
|
//
|
|
// LPDRVCONFIGINFO pdci: Pointer to optional DRVCONFIGINFO structure.
|
|
// If this argument is NULL, then the codec should invent its own
|
|
// storage location.
|
|
//
|
|
// Return (LRESULT):
|
|
// A non-zero return values specifies that either configuration is
|
|
// supported or that the dialog was successfully displayed and
|
|
// dismissed. A zero return indicates either configuration is not
|
|
// supported or some other failure.
|
|
//
|
|
// History:
|
|
// 1/25/93 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdDriverConfigure
|
|
(
|
|
PCODECINST pci,
|
|
HWND hwnd,
|
|
LPDRVCONFIGINFO pdci
|
|
)
|
|
{
|
|
//
|
|
// first check to see if we are only being queried for hardware
|
|
// configuration support. if hwnd == (HWND)-1 then we are being
|
|
// queried and should return zero for 'not supported' and non-zero
|
|
// for 'supported'.
|
|
//
|
|
if (hwnd == (HWND)-1)
|
|
{
|
|
//
|
|
// this codec does not support hardware configuration so return
|
|
// zero...
|
|
//
|
|
return (0L);
|
|
}
|
|
|
|
//
|
|
// we are being asked to bring up our hardware configuration dialog.
|
|
// if this codec can bring up a dialog box, then after the dialog
|
|
// is dismissed we return non-zero. if we are not able to display a
|
|
// dialog, then return zero.
|
|
//
|
|
return (0L);
|
|
} // acmdDriverConfigure()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdDriverDetails
|
|
//
|
|
// Description:
|
|
// This function handles the ACMDM_DRIVER_DETAILS message. The codec
|
|
// is responsible for filling in the ACMDRIVERDETAILS structure with
|
|
// various information.
|
|
//
|
|
// NOTE! It is *VERY* important that you fill in your ACMDRIVERDETAILS
|
|
// structure correctly. The ACM and applications must be able to
|
|
// rely on this information.
|
|
//
|
|
// WARNING! The _reserved_ bits of any fields of the ACMDRIVERDETAILS
|
|
// structure are _exactly that_: RESERVED. Do NOT use any extra
|
|
// flag bits, etc. for custom information. The proper way to add
|
|
// custom capabilities to your codec is this:
|
|
//
|
|
// o define a new message in the ACMDM_USER range.
|
|
//
|
|
// o an application that wishes to use one of these extra features
|
|
// should then:
|
|
//
|
|
// o open the codec with acmConverterOpen.
|
|
//
|
|
// o check for the proper uMid and uPid using acmConverterInfo
|
|
//
|
|
// o send the 'user defined' message with acmConverterMessage
|
|
// to retrieve additional information, etc.
|
|
//
|
|
// o close the codec with acmConverterClose.
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci: Pointer to private codec instance structure.
|
|
//
|
|
// LPACMDRIVERDETAILS padd: Pointer to ACMDRIVERDETAILS structure to fill in
|
|
// for caller. This structure may be larger or smaller than the
|
|
// current definition of ACMDRIVERDETAILS--cbStruct specifies the valid
|
|
// size.
|
|
//
|
|
// Return (LRESULT):
|
|
// The return value is zero (MMSYSERR_NOERROR) for success. A non-zero
|
|
// return signifies an error which is either an MMSYSERR_* or an
|
|
// ACMERR_*.
|
|
//
|
|
// Note that this function should never fail. There are two possible
|
|
// error conditions:
|
|
//
|
|
// o if padd is NULL or an invalid pointer.
|
|
//
|
|
// o if cbStruct is less than four; in this case, there is not enough
|
|
// room to return the number of bytes filled in.
|
|
//
|
|
// Because these two error conditions are easily defined, the ACM
|
|
// will catch these errors. The codec does not need to check for these
|
|
// conditions.
|
|
//
|
|
// History:
|
|
// 1/23/93 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdDriverDetails
|
|
(
|
|
PCODECINST pci,
|
|
LPACMDRIVERDETAILS padd
|
|
)
|
|
{
|
|
ACMDRIVERDETAILS add;
|
|
DWORD cbStruct;
|
|
|
|
//
|
|
// it is easiest to fill in a temporary structure with valid info
|
|
// and then copy the requested number of bytes to the destination
|
|
// buffer.
|
|
//
|
|
cbStruct = min(padd->cbStruct, sizeof(ACMDRIVERDETAILS));
|
|
add.cbStruct = cbStruct;
|
|
|
|
//
|
|
// for the current implementation of an ACM codec, the fccType and
|
|
// fccComp members *MUST* always be ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC ('audc')
|
|
// and ACMDRIVERDETAILS_FCCCOMP_UNDEFINED (0) respectively.
|
|
//
|
|
add.fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
|
|
add.fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
|
|
|
|
//
|
|
// the manufacturer id (uMid) and product id (uPid) must be filled
|
|
// in with your company's _registered_ id's. for more information
|
|
// on these id's and how to get them registered contact Microsoft
|
|
// and get the Multimedia Developer Registration Kit:
|
|
//
|
|
// Microsoft Corporation
|
|
// Multimedia Systems Group
|
|
// Product Marketing
|
|
// One Microsoft Way
|
|
// Redmond, WA 98052-6399
|
|
//
|
|
// Phone: 800-227-4679 x11771
|
|
//
|
|
// note that during the development phase or your codec, you may
|
|
// use the reserved value of '0' for both uMid and uPid.
|
|
//
|
|
add.wMid = MM_MICROSOFT;
|
|
add.wPid = MM_MSFT_ACM_MSADPCM;
|
|
|
|
//
|
|
// the vdwACM and vdwDriver members contain version information for
|
|
// the driver.
|
|
//
|
|
// vdwACM must contain the version of the *ACM* that the codec was
|
|
// designed for.
|
|
//
|
|
// vdwDriver is the version of the driver.
|
|
//
|
|
add.vdwACM = VERSION_MSACM;
|
|
add.vdwDriver = VERSION_CODEC;
|
|
|
|
//
|
|
// the following flags are used to specify the type of conversion(s)
|
|
// that the converter/codec/filter supports. these are placed in the
|
|
// fdwSupport field of the ACMDRIVERDETAILS structure. note that a converter
|
|
// can support one or more of these flags in any combination.
|
|
//
|
|
// ACMDRIVERDETAILS_SUPPORTF_CODEC: this flag is set if the converter supports
|
|
// conversions from one format tag to another format tag. for example,
|
|
// if a converter compresses WAVE_FORMAT_PCM to WAVE_FORMAT_ADPCM, then
|
|
// this bit should be set.
|
|
//
|
|
// ACMDRIVERDETAILS_SUPPORTF_CONVERTER: this flags is set if the converter
|
|
// supports conversions on the same format tag. as an example, the PCM
|
|
// converter that is built into the ACM sets this bit (and only this
|
|
// bit) because it converts only PCM formats (bits, sample rate).
|
|
//
|
|
// ACMDRIVERDETAILS_SUPPORTF_FILTER: this flag is set if the converter supports
|
|
// 'in place' transformations on a single format tag without changing
|
|
// the size of the resulting data. for example, a converter that changed
|
|
// the 'volume' of PCM data would set this bit. note that this is a
|
|
// _destructive_ action--but it saves memory, etc.
|
|
//
|
|
// this converter only supports compression and decompression.
|
|
//
|
|
add.fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
|
|
|
|
|
|
//
|
|
// Return the number of format tags this converter supports
|
|
// (In the case of PCM only this is 1)
|
|
//
|
|
add.cFormatTags = CODEC_MAX_FORMAT_TAGS;
|
|
|
|
//
|
|
// Return the number of filter tags this converter supports
|
|
// (In the case of a codec (only) it is 0)
|
|
//
|
|
add.cFilterTags = CODEC_MAX_FILTER_TAGS;
|
|
|
|
|
|
|
|
//
|
|
// the remaining members in the ACMDRIVERDETAILS structure are sometimes
|
|
// not needed. because of this we make a quick check to see if we
|
|
// should go through the effort of filling in these members.
|
|
//
|
|
if (FIELD_OFFSET(ACMDRIVERDETAILS, hicon) < cbStruct)
|
|
{
|
|
//
|
|
// this codec has no custom icon
|
|
//
|
|
add.hicon = NULL;
|
|
|
|
LoadStringCodec(pci->hinst, IDS_CODEC_SHORTNAME, add.szShortName, SIZEOFACMSTR(add.szShortName));
|
|
LoadStringCodec(pci->hinst, IDS_CODEC_LONGNAME, add.szLongName, SIZEOFACMSTR(add.szLongName));
|
|
|
|
if (FIELD_OFFSET(ACMDRIVERDETAILS, szCopyright) < cbStruct)
|
|
{
|
|
LoadStringCodec(pci->hinst, IDS_CODEC_COPYRIGHT, add.szCopyright, SIZEOFACMSTR(add.szCopyright));
|
|
LoadStringCodec(pci->hinst, IDS_CODEC_LICENSING, add.szLicensing, SIZEOFACMSTR(add.szLicensing));
|
|
LoadStringCodec(pci->hinst, IDS_CODEC_FEATURES, add.szFeatures, SIZEOFACMSTR(add.szFeatures));
|
|
}
|
|
}
|
|
|
|
//
|
|
// now copy the correct number of bytes to the caller's buffer
|
|
//
|
|
_fmemcpy(padd, &add, (UINT)add.cbStruct);
|
|
|
|
//
|
|
// success!
|
|
//
|
|
return (MMSYSERR_NOERROR);
|
|
} // acmdDriverDetails()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdDriverAbout
|
|
//
|
|
// Description:
|
|
// This function is called to handle the ACMDM_DRIVER_ABOUT message.
|
|
// A codec has the option of displaying its own 'about box' or letting
|
|
// the ACM display one for it.
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci: Pointer to private codec instance structure.
|
|
//
|
|
// HWND hwnd: Handle to parent window to use when displaying custom
|
|
// about box. If a codec displays its own dialog, it is _required_
|
|
// to display a modal dialog box using this hwnd argument as the
|
|
// parent.
|
|
//
|
|
// Return (LRESULT):
|
|
// The return value is MMSYSERR_NOTSUPPORTED if the ACM should display
|
|
// a generic about box using the information contained in the codec
|
|
// capabilities structure.
|
|
//
|
|
// If the codec chooses to display its own dialog box, then after
|
|
// the dialog is dismissed by the user, MMSYSERR_NOERROR should be
|
|
// returned.
|
|
//
|
|
// History:
|
|
// 1/24/93 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdDriverAbout
|
|
(
|
|
PCODECINST pci,
|
|
HWND hwnd
|
|
)
|
|
{
|
|
//
|
|
// this codec does not need any special dialog, so tell the ACM to
|
|
// display one for us. note that this is the _recommended_ method
|
|
// for consistency and simplicity of codecs. why write code when
|
|
// you don't have to?
|
|
//
|
|
return (MMSYSERR_NOTSUPPORTED);
|
|
} // acmdDriverAbout()
|
|
|
|
|
|
//==========================================================================;
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================;
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdFormatSuggest
|
|
//
|
|
// Description:
|
|
// This function handles the ACMDM_FORMAT_SUGGEST message. The purpose
|
|
// of this function is to provide a way for the ACM (wave mapper) or
|
|
// an application to quickly get a destination format that this codec
|
|
// can convert the source format to. The 'suggested' format should
|
|
// be as close to a common format as possible.
|
|
//
|
|
// Another way to think about this message is: what format would this
|
|
// codec like to convert the source format to?
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci: Pointer to private codec instance structure.
|
|
//
|
|
// LPACMDRVFORMATSUGGEST padfs: Pointer to ACMDRVFORMATSUGGEST structure.
|
|
//
|
|
// Return (LRESULT):
|
|
// The return value is zero (MMSYSERR_NOERROR) if this function
|
|
// succeeds with no errors. The return value is a non-zero ACMERR_*
|
|
// or MMSYSERR_* if the function fails.
|
|
//
|
|
// History:
|
|
// 11/28/92 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdFormatSuggest
|
|
(
|
|
PCODECINST pci,
|
|
LPACMDRVFORMATSUGGEST padfs
|
|
)
|
|
{
|
|
LPWAVEFORMATEX pwfxSrc;
|
|
LPWAVEFORMATEX pwfxDst;
|
|
LPADPCMWAVEFORMAT pwfadpcm;
|
|
LPPCMWAVEFORMAT pwfpcm;
|
|
|
|
pwfxSrc = padfs->pwfxSrc;
|
|
pwfxDst = padfs->pwfxDst;
|
|
|
|
//
|
|
//
|
|
//
|
|
//
|
|
switch (pwfxSrc->wFormatTag)
|
|
{
|
|
case WAVE_FORMAT_PCM:
|
|
//
|
|
// verify source format is acceptable for this driver
|
|
//
|
|
if (!pcmIsValidFormat(pwfxSrc))
|
|
break;
|
|
|
|
|
|
//
|
|
// Verify that you are not asking for a particular dest format
|
|
// that is not ADPCM.
|
|
//
|
|
if( (padfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG) &&
|
|
(pwfxDst->wFormatTag != WAVE_FORMAT_ADPCM) ) {
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
// Verify that if other restrictions are given, they
|
|
// match to the source. (Since we do not convert
|
|
// the nChannels or nSamplesPerSec
|
|
if( (padfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS) &&
|
|
(pwfxSrc->nChannels != pwfxDst->nChannels) ) {
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
if( (padfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC) &&
|
|
(pwfxSrc->nSamplesPerSec != pwfxDst->nSamplesPerSec) ) {
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
// Verify that if we are asking for a specific number of bits
|
|
// per sample, that it is the correct #
|
|
if( (padfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE) &&
|
|
(pwfxDst->wBitsPerSample != 4) ) {
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
//
|
|
// suggest an ADPCM format that has most of the same details
|
|
// as the source PCM format
|
|
//
|
|
pwfxDst->wFormatTag = WAVE_FORMAT_ADPCM;
|
|
pwfxDst->nSamplesPerSec = pwfxSrc->nSamplesPerSec;
|
|
pwfxDst->nChannels = pwfxSrc->nChannels;
|
|
pwfxDst->wBitsPerSample = MSADPCM_BITS_PER_SAMPLE;
|
|
|
|
pwfxDst->nBlockAlign = adpcmBlockAlign(pwfxDst);
|
|
pwfxDst->nAvgBytesPerSec = adpcmAvgBytesPerSec(pwfxDst);
|
|
pwfxDst->cbSize = MSADPCM_WFX_EXTRA_BYTES;
|
|
|
|
pwfadpcm = (LPADPCMWAVEFORMAT)pwfxDst;
|
|
pwfadpcm->wSamplesPerBlock = adpcmSamplesPerBlock(pwfxDst);
|
|
|
|
adpcmCopyCoefficients(pwfadpcm);
|
|
return (MMSYSERR_NOERROR);
|
|
|
|
|
|
case WAVE_FORMAT_ADPCM:
|
|
//
|
|
// verify source format is acceptable for this driver
|
|
//
|
|
if (!adpcmIsValidFormat(pwfxSrc) ||
|
|
!adpcmIsMagicFormat((LPADPCMWAVEFORMAT)pwfxSrc))
|
|
break;
|
|
|
|
//
|
|
// Verify that you are not asking for a particular dest format
|
|
// that is not PCM.
|
|
//
|
|
if( (padfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG) &&
|
|
(pwfxDst->wFormatTag != WAVE_FORMAT_PCM) ) {
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
// Verify that if other restrictions are given, they
|
|
// match to the source. (Since we do not convert
|
|
// the nChannels or nSamplesPerSec
|
|
if( (padfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS) &&
|
|
(pwfxSrc->nChannels != pwfxDst->nChannels) ) {
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
if( (padfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC) &&
|
|
(pwfxSrc->nSamplesPerSec != pwfxDst->nSamplesPerSec) ) {
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
//
|
|
// suggest a PCM format that has most of the same details
|
|
// as the source ADPCM format
|
|
//
|
|
pwfxDst->wFormatTag = WAVE_FORMAT_PCM;
|
|
pwfxDst->nSamplesPerSec = pwfxSrc->nSamplesPerSec;
|
|
pwfxDst->nChannels = pwfxSrc->nChannels;
|
|
|
|
// Verify that if we are asking for a specific number of bits
|
|
// per sample, that it is the correct #
|
|
if( padfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE ) {
|
|
if( (pwfxDst->wBitsPerSample != 8) &&
|
|
(pwfxDst->wBitsPerSample != 16) ) {
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
} else {
|
|
// Default to 16 bit decode
|
|
pwfxDst->wBitsPerSample = 16;
|
|
}
|
|
|
|
pwfpcm = (LPPCMWAVEFORMAT)pwfxDst;
|
|
|
|
pwfxDst->nBlockAlign = PCM_BLOCKALIGNMENT(pwfxDst);
|
|
pwfxDst->nAvgBytesPerSec = pwfxDst->nSamplesPerSec *
|
|
pwfxDst->nBlockAlign;
|
|
return (MMSYSERR_NOERROR);
|
|
}
|
|
|
|
//
|
|
// can't suggest anything because either the source format is foreign
|
|
// or the destination format has restrictions that this converter
|
|
// cannot deal with.
|
|
//
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
} // acmdFormatSuggest()
|
|
|
|
|
|
//==========================================================================;
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================;
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdFormatTagDetails
|
|
//
|
|
// Description:
|
|
//
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci:
|
|
//
|
|
// LPACMFORMATTAGDETAILS padft:
|
|
//
|
|
// DWORD fdwDetails:
|
|
//
|
|
// Return (LRESULT):
|
|
//
|
|
// History:
|
|
// 08/01/93 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdFormatTagDetails
|
|
(
|
|
PCODECINST pci,
|
|
LPACMFORMATTAGDETAILS padft,
|
|
DWORD fdwDetails
|
|
)
|
|
{
|
|
UINT uFormatTag;
|
|
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
switch (ACM_FORMATTAGDETAILSF_QUERYMASK & fdwDetails)
|
|
{
|
|
case ACM_FORMATTAGDETAILSF_INDEX:
|
|
//
|
|
// if the index is too large, then they are asking for a
|
|
// non-existant format. return error.
|
|
//
|
|
if (CODEC_MAX_FORMAT_TAGS <= padft->dwFormatTagIndex)
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
|
|
uFormatTag = gauFormatTagIndexToTag[(UINT)padft->dwFormatTagIndex];
|
|
break;
|
|
|
|
|
|
case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
|
|
switch (padft->dwFormatTag)
|
|
{
|
|
case WAVE_FORMAT_UNKNOWN:
|
|
case WAVE_FORMAT_ADPCM:
|
|
uFormatTag = WAVE_FORMAT_ADPCM;
|
|
break;
|
|
|
|
case WAVE_FORMAT_PCM:
|
|
uFormatTag = WAVE_FORMAT_PCM;
|
|
break;
|
|
|
|
default:
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
break;
|
|
|
|
|
|
case ACM_FORMATTAGDETAILSF_FORMATTAG:
|
|
switch (padft->dwFormatTag)
|
|
{
|
|
case WAVE_FORMAT_ADPCM:
|
|
uFormatTag = WAVE_FORMAT_ADPCM;
|
|
break;
|
|
|
|
case WAVE_FORMAT_PCM:
|
|
uFormatTag = WAVE_FORMAT_PCM;
|
|
break;
|
|
|
|
default:
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
break;
|
|
|
|
|
|
//
|
|
// if this converter does not understand a query type, then
|
|
// return 'not supported'
|
|
//
|
|
default:
|
|
return (MMSYSERR_NOTSUPPORTED);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
//
|
|
//
|
|
switch (uFormatTag)
|
|
{
|
|
case WAVE_FORMAT_PCM:
|
|
padft->dwFormatTagIndex = 0;
|
|
padft->dwFormatTag = WAVE_FORMAT_PCM;
|
|
padft->cbFormatSize = sizeof(PCMWAVEFORMAT);
|
|
padft->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
|
|
padft->cStandardFormats = CODEC_MAX_FORMATS_PCM;
|
|
|
|
|
|
//
|
|
// the ACM is responsible for the PCM format tag name
|
|
//
|
|
padft->szFormatTag[0] = '\0';
|
|
break;
|
|
|
|
case WAVE_FORMAT_ADPCM:
|
|
padft->dwFormatTagIndex = 1;
|
|
padft->dwFormatTag = WAVE_FORMAT_ADPCM;
|
|
padft->cbFormatSize = sizeof(WAVEFORMATEX) +
|
|
MSADPCM_WFX_EXTRA_BYTES;
|
|
padft->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
|
|
padft->cStandardFormats = CODEC_MAX_FORMATS_ADPCM;
|
|
|
|
LoadStringCodec(pci->hinst, IDS_CODEC_NAME, padft->szFormatTag, SIZEOFACMSTR(padft->szFormatTag));
|
|
break;
|
|
|
|
default:
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
|
|
//
|
|
// return only the requested info
|
|
//
|
|
// the ACM will guarantee that the ACMFORMATTAGDETAILS structure
|
|
// passed is at least large enough to hold the base information of
|
|
// the details structure
|
|
//
|
|
padft->cbStruct = min(padft->cbStruct, sizeof(*padft));
|
|
|
|
|
|
//
|
|
//
|
|
//
|
|
return (MMSYSERR_NOERROR);
|
|
} // acmdFormatTagDetails()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdFormatDetails
|
|
//
|
|
// Description:
|
|
//
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci:
|
|
//
|
|
// LPACMFORMATDETAILS padf:
|
|
//
|
|
// DWORD fdwDetails:
|
|
//
|
|
// Return (LRESULT):
|
|
//
|
|
// History:
|
|
// 06/13/93 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdFormatDetails
|
|
(
|
|
PCODECINST pci,
|
|
LPACMFORMATDETAILS padf,
|
|
DWORD fdwDetails
|
|
)
|
|
{
|
|
LPWAVEFORMATEX pwfx;
|
|
LPADPCMWAVEFORMAT pwfadpcm;
|
|
UINT uFormatIndex;
|
|
UINT u;
|
|
|
|
pwfx = padf->pwfx;
|
|
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
switch (ACM_FORMATDETAILSF_QUERYMASK & fdwDetails)
|
|
{
|
|
case ACM_FORMATDETAILSF_INDEX:
|
|
//
|
|
// enumerate by index
|
|
//
|
|
// for this converter, this is more code than necessary... just
|
|
// verify that the format tag is something we know about
|
|
//
|
|
switch (padf->dwFormatTag)
|
|
{
|
|
case WAVE_FORMAT_PCM:
|
|
if (CODEC_MAX_FORMATS_PCM <= padf->dwFormatIndex)
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
|
|
//
|
|
// put some stuff in more accessible variables--note
|
|
// that we bring variable sizes down to a reasonable
|
|
// size for 16 bit code...
|
|
//
|
|
uFormatIndex = (UINT)padf->dwFormatIndex;
|
|
pwfx = padf->pwfx;
|
|
|
|
//
|
|
// now fill in the format structure
|
|
//
|
|
pwfx->wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
u = uFormatIndex / (CODEC_MAX_BITSPERSAMPLE_PCM * CODEC_MAX_CHANNELS);
|
|
pwfx->nSamplesPerSec = gauFormatIndexToSampleRate[u];
|
|
|
|
u = uFormatIndex % CODEC_MAX_CHANNELS;
|
|
pwfx->nChannels = u + 1;
|
|
|
|
u = (uFormatIndex / CODEC_MAX_CHANNELS) % CODEC_MAX_CHANNELS;
|
|
pwfx->wBitsPerSample = (WORD)gauFormatIndexToBitsPerSample[u];
|
|
|
|
pwfx->nBlockAlign = PCM_BLOCKALIGNMENT(pwfx);
|
|
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
|
|
|
|
//
|
|
// note that the cbSize field is NOT valid for PCM
|
|
// formats
|
|
//
|
|
// pwfx->cbSize = 0;
|
|
break;
|
|
|
|
|
|
case WAVE_FORMAT_ADPCM:
|
|
if (CODEC_MAX_FORMATS_ADPCM <= padf->dwFormatIndex)
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
|
|
//
|
|
// put some stuff in more accessible variables--note that we
|
|
// bring variable sizes down to a reasonable size for 16 bit
|
|
// code...
|
|
//
|
|
uFormatIndex = (UINT)padf->dwFormatIndex;
|
|
pwfx = padf->pwfx;
|
|
pwfadpcm = (LPADPCMWAVEFORMAT)pwfx;
|
|
|
|
//
|
|
//
|
|
//
|
|
pwfx->wFormatTag = WAVE_FORMAT_ADPCM;
|
|
|
|
u = uFormatIndex / (CODEC_MAX_BITSPERSAMPLE_ADPCM * CODEC_MAX_CHANNELS);
|
|
pwfx->nSamplesPerSec = gauFormatIndexToSampleRate[u];
|
|
|
|
u = uFormatIndex % CODEC_MAX_CHANNELS;
|
|
pwfx->nChannels = u + 1;
|
|
pwfx->wBitsPerSample = MSADPCM_BITS_PER_SAMPLE;
|
|
|
|
pwfx->nBlockAlign = adpcmBlockAlign(pwfx);
|
|
pwfx->nAvgBytesPerSec = adpcmAvgBytesPerSec(pwfx);
|
|
pwfx->cbSize = MSADPCM_WFX_EXTRA_BYTES;
|
|
|
|
pwfadpcm->wSamplesPerBlock = adpcmSamplesPerBlock(pwfx);
|
|
|
|
adpcmCopyCoefficients(pwfadpcm);
|
|
break;
|
|
|
|
default:
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
case ACM_FORMATDETAILSF_FORMAT:
|
|
//
|
|
// must want to verify that the format passed in is supported
|
|
// and return a string description...
|
|
//
|
|
switch (pwfx->wFormatTag)
|
|
{
|
|
case WAVE_FORMAT_PCM:
|
|
if (!pcmIsValidFormat(pwfx))
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
break;
|
|
|
|
case WAVE_FORMAT_ADPCM:
|
|
if (!adpcmIsValidFormat(pwfx) ||
|
|
!adpcmIsMagicFormat((LPADPCMWAVEFORMAT)pwfx))
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
break;
|
|
|
|
default:
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
//
|
|
// don't know how to do the query type passed--return 'not
|
|
// supported'.
|
|
//
|
|
return (MMSYSERR_NOTSUPPORTED);
|
|
}
|
|
|
|
|
|
//
|
|
// return only the requested info
|
|
//
|
|
// the ACM will guarantee that the ACMFORMATDETAILS structure
|
|
// passed is at least large enough to hold the base structure
|
|
//
|
|
// note that we let the ACM create the format string for us since
|
|
// we require no special formatting (and don't want to deal with
|
|
// internationalization issues, etc)
|
|
//
|
|
padf->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
|
|
padf->szFormat[0] = '\0';
|
|
padf->cbStruct = min(padf->cbStruct, sizeof(*padf));
|
|
|
|
|
|
//
|
|
//
|
|
//
|
|
return (MMSYSERR_NOERROR);
|
|
} // acmdFormatDetails()
|
|
|
|
|
|
//==========================================================================;
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================;
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdStreamQuery
|
|
//
|
|
// Description:
|
|
// This is an internal helper used by the ACMDM_STREM_OPEN
|
|
// and ACMDM_STREAM_SIZE messages.
|
|
// The purpose of this function is to tell the caller if the proposed
|
|
// conversion can be handled by this codec.
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci: Pointer to private codec instance structure.
|
|
//
|
|
// LPWAVEFORMATEX pwfxSrc:
|
|
//
|
|
// LPWAVEFORMATEX pwfxDst:
|
|
//
|
|
// LPWAVEFILTER pwfltr:
|
|
//
|
|
// DWORD fdwOpen:
|
|
//
|
|
// Return (LRESULT):
|
|
// The return value is zero (MMSYSERR_NOERROR) if this function
|
|
// succeeds with no errors. The return value is a non-zero ACMERR_*
|
|
// or MMSYSERR_* if the function fails.
|
|
//
|
|
// A return value of ACMERR_NOTPOSSIBLE must be returned if the conversion
|
|
// cannot be performed by this codec.
|
|
//
|
|
// History:
|
|
// 11/28/92 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdStreamQuery
|
|
(
|
|
PCODECINST pci,
|
|
LPWAVEFORMATEX pwfxSrc,
|
|
LPWAVEFORMATEX pwfxDst,
|
|
LPWAVEFILTER pwfltr,
|
|
DWORD fdwOpen
|
|
)
|
|
{
|
|
LPADPCMWAVEFORMAT pwfADPCM;
|
|
LPPCMWAVEFORMAT pwfPCM;
|
|
|
|
//
|
|
// check to see if this
|
|
// codec can convert from the source to the destination.
|
|
//
|
|
// first check if source is ADPCM so destination must be PCM..
|
|
//
|
|
if (adpcmIsValidFormat(pwfxSrc))
|
|
{
|
|
if (!pcmIsValidFormat(pwfxDst))
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
|
|
//
|
|
// converting from ADPCM to PCM...
|
|
//
|
|
pwfADPCM = (LPADPCMWAVEFORMAT)pwfxSrc;
|
|
pwfPCM = (LPPCMWAVEFORMAT)pwfxDst;
|
|
|
|
if (pwfADPCM->wfx.nChannels != pwfPCM->wf.nChannels)
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
|
|
if (pwfADPCM->wfx.nSamplesPerSec != pwfPCM->wf.nSamplesPerSec)
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
|
|
if (!adpcmIsMagicFormat(pwfADPCM))
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
|
|
return (MMSYSERR_NOERROR);
|
|
}
|
|
|
|
//
|
|
// now try source as PCM so destination must be ADPCM..
|
|
//
|
|
else if (pcmIsValidFormat(pwfxSrc))
|
|
{
|
|
if (!adpcmIsValidFormat(pwfxDst))
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
|
|
//
|
|
// converting from PCM to ADPCM...
|
|
//
|
|
pwfPCM = (LPPCMWAVEFORMAT)pwfxSrc;
|
|
pwfADPCM = (LPADPCMWAVEFORMAT)pwfxDst;
|
|
|
|
if (pwfADPCM->wfx.nChannels != pwfPCM->wf.nChannels)
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
|
|
if (pwfADPCM->wfx.nSamplesPerSec != pwfPCM->wf.nSamplesPerSec)
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
|
|
if (!adpcmIsMagicFormat(pwfADPCM))
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
|
|
return (MMSYSERR_NOERROR);
|
|
}
|
|
|
|
//
|
|
// we are unable to perform the conversion we are being queried for
|
|
// so return ACMERR_NOTPOSSIBLE to signify this...
|
|
//
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
} // acmdStreamQuery()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdStreamOpen
|
|
//
|
|
// Description:
|
|
// This function handles the ACMDM_STREAM_OPEN message. This message
|
|
// is sent to initiate a new conversion stream. This is usually caused
|
|
// by an application calling acmOpenConversion. If this function is
|
|
// successful, then one or more ACMDM_STREAM_CONVERT messages will be
|
|
// sent to convert individual buffers (user calls acmStreamConvert).
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci: Pointer to private codec instance structure.
|
|
//
|
|
// LPACMDRVINSTANCE padi: Pointer to instance data for the conversion
|
|
// stream. This structure was allocated by the ACM and filled with
|
|
// the most common instance data needed for conversions.
|
|
//
|
|
// Return (LRESULT):
|
|
// The return value is zero (MMSYSERR_NOERROR) if this function
|
|
// succeeds with no errors. The return value is a non-zero ACMERR_*
|
|
// or MMSYSERR_* if the function fails.
|
|
//
|
|
// History:
|
|
// 11/28/92 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdStreamOpen
|
|
(
|
|
PCODECINST pci,
|
|
LPACMDRVSTREAMINSTANCE padsi
|
|
)
|
|
{
|
|
LPWAVEFORMATEX pwfxSrc;
|
|
LPWAVEFORMATEX pwfxDst;
|
|
|
|
pwfxSrc = padsi->pwfxSrc;
|
|
pwfxDst = padsi->pwfxDst;
|
|
|
|
//
|
|
// the most important condition to check before doing anything else
|
|
// is that this codec can actually perform the conversion we are
|
|
// being opened for. this check should fail as quickly as possible
|
|
// if the conversion is not possible by this codec.
|
|
//
|
|
// it is VERY important to fail quickly so the ACM can attempt to
|
|
// find a codec that is suitable for the conversion. also note that
|
|
// the ACM may call this codec several times with slightly different
|
|
// format specifications before giving up.
|
|
//
|
|
// this codec first verifies that the src and dst formats are
|
|
// acceptable...
|
|
//
|
|
if (acmdStreamQuery(pci,
|
|
pwfxSrc,
|
|
pwfxDst,
|
|
padsi->pwfltr,
|
|
padsi->fdwOpen))
|
|
{
|
|
//
|
|
// either the source or destination format is illegal for this
|
|
// codec--or the conversion between the formats can not be
|
|
// performed by this codec.
|
|
//
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
|
|
//
|
|
// we have decided that this codec can handle the conversion stream.
|
|
// so we want to do _as much work as possible_ right now to prepare
|
|
// for converting data. any resource allocation, table building, etc
|
|
// that can be dealt with at this time should be done.
|
|
//
|
|
// THIS IS VERY IMPORTANT! all ACMDM_STREAM_CONVERT messages need to
|
|
// be handled as quickly as possible.
|
|
//
|
|
// this codec is very simple, so we only figure out what conversion
|
|
// function that should be used for converting from the src format
|
|
// to the dst format and place this in the dwDrvInstance member
|
|
// of the ACMDRVINSTANCE structure. we then only need to 'call'
|
|
// this function during the ACMDM_STREAM_CONVERT message.
|
|
//
|
|
if (pwfxSrc->wFormatTag == WAVE_FORMAT_ADPCM)
|
|
{
|
|
#ifdef WIN32
|
|
switch (pwfxDst->nChannels)
|
|
{
|
|
case 1:
|
|
if (8 == pwfxDst->wBitsPerSample)
|
|
padsi->dwDriver = (DWORD_PTR)adpcmDecode4Bit_M08;
|
|
else
|
|
padsi->dwDriver = (DWORD_PTR)adpcmDecode4Bit_M16;
|
|
break;
|
|
|
|
case 2:
|
|
if (8 == pwfxDst->wBitsPerSample)
|
|
padsi->dwDriver = (DWORD_PTR)adpcmDecode4Bit_S08;
|
|
else
|
|
padsi->dwDriver = (DWORD_PTR)adpcmDecode4Bit_S16;
|
|
break;
|
|
|
|
default:
|
|
return ACMERR_NOTPOSSIBLE;
|
|
}
|
|
#else
|
|
padsi->dwDriver = (DWORD_PTR)DecodeADPCM_4Bit_386;
|
|
#endif
|
|
return (MMSYSERR_NOERROR);
|
|
}
|
|
else if (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM)
|
|
{
|
|
//
|
|
// Check to see if we will be doing this conversion in realtime.
|
|
// (The default is yes)
|
|
//
|
|
if (padsi->fdwOpen & ACM_STREAMOPENF_NONREALTIME)
|
|
{
|
|
switch (pwfxSrc->nChannels)
|
|
{
|
|
case 1:
|
|
if (8 == pwfxSrc->wBitsPerSample)
|
|
padsi->dwDriver = (DWORD_PTR)adpcmEncode4Bit_M08_FullPass;
|
|
else
|
|
padsi->dwDriver = (DWORD_PTR)adpcmEncode4Bit_M16_FullPass;
|
|
break;
|
|
|
|
case 2:
|
|
if (8 == pwfxSrc->wBitsPerSample)
|
|
padsi->dwDriver = (DWORD_PTR)adpcmEncode4Bit_S08_FullPass;
|
|
else
|
|
padsi->dwDriver = (DWORD_PTR)adpcmEncode4Bit_S16_FullPass;
|
|
break;
|
|
|
|
default:
|
|
return ACMERR_NOTPOSSIBLE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef WIN32
|
|
switch (pwfxSrc->nChannels)
|
|
{
|
|
case 1:
|
|
if (8 == pwfxSrc->wBitsPerSample)
|
|
padsi->dwDriver = (DWORD_PTR)adpcmEncode4Bit_M08_OnePass;
|
|
else
|
|
padsi->dwDriver = (DWORD_PTR)adpcmEncode4Bit_M16_OnePass;
|
|
break;
|
|
|
|
case 2:
|
|
if (8 == pwfxSrc->wBitsPerSample)
|
|
padsi->dwDriver = (DWORD_PTR)adpcmEncode4Bit_S08_OnePass;
|
|
else
|
|
padsi->dwDriver = (DWORD_PTR)adpcmEncode4Bit_S16_OnePass;
|
|
break;
|
|
|
|
default:
|
|
return ACMERR_NOTPOSSIBLE;
|
|
}
|
|
#else
|
|
padsi->dwDriver = (DWORD_PTR)EncodeADPCM_4Bit_386;
|
|
#endif
|
|
}
|
|
|
|
return (MMSYSERR_NOERROR);
|
|
}
|
|
|
|
//
|
|
// fail--we cannot perform the conversion
|
|
//
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
} // acmdStreamOpen()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdStreamClose
|
|
//
|
|
// Description:
|
|
// This function is called to handle the ACMDM_STREAM_CLOSE message.
|
|
// This message is sent when a conversion stream is no longer being
|
|
// used (the stream is being closed; usually by an application
|
|
// calling acmCloseConversion). The codec should clean up any resources
|
|
// that were allocated for the stream.
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci: Pointer to private codec instance structure.
|
|
//
|
|
// LPACMDRVINSTANCE padi: Pointer to instance data for the conversion
|
|
// stream.
|
|
//
|
|
// Return (LRESULT):
|
|
// The return value is zero (MMSYSERR_NOERROR) if this function
|
|
// succeeds with no errors. The return value is a non-zero ACMERR_*
|
|
// or MMSYSERR_* if the function fails.
|
|
//
|
|
// NOTE! It is _strongly_ recommended that a codec not fail to close
|
|
// a conversion stream.
|
|
//
|
|
// History:
|
|
// 11/28/92 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdStreamClose
|
|
(
|
|
PCODECINST pci,
|
|
LPACMDRVSTREAMINSTANCE padsi
|
|
)
|
|
{
|
|
//
|
|
// the codec should clean up all resources that were allocated for
|
|
// the stream instance.
|
|
//
|
|
// this codec did not allocate any resources, so we succeed immediately
|
|
//
|
|
return (MMSYSERR_NOERROR);
|
|
} // acmdStreamClose()
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdStreamSize
|
|
//
|
|
// Description:
|
|
// This function handles the ACMDM_STREAM_SIZE message. The purpose
|
|
// of this function is to provide the _largest size in bytes_ that
|
|
// the source or destination buffer needs to be given the input and
|
|
// output formats and the size in bytes of the source or destination
|
|
// data buffer.
|
|
//
|
|
// In other words: how big does my destination buffer need to be to
|
|
// hold the converted data? (ACM_STREAMSIZEF_SOURCE)
|
|
//
|
|
// Or: how big can my source buffer be given the destination buffer?
|
|
// (ACM_STREAMSIZEF_DESTINATION)
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci: Pointer to private ACM driver instance structure.
|
|
// This structure is [optionally] allocated during the DRV_OPEN message
|
|
// which is handled by the acmdDriverOpen function.
|
|
//
|
|
// LPACMDRVSTREAMINSTANCE padsi: Pointer to instance data for the
|
|
// conversion stream. This structure was allocated by the ACM and
|
|
// filled with the most common instance data needed for conversions.
|
|
// The information in this structure is exactly the same as it was
|
|
// during the ACMDM_STREAM_OPEN message--so it is not necessary
|
|
// to re-verify the information referenced by this structure.
|
|
//
|
|
// LPACMDRVSTREAMSIZE padss: Specifies a pointer to the ACMDRVSTREAMSIZE
|
|
// structure that defines the conversion stream size query attributes.
|
|
//
|
|
// Return (LRESULT):
|
|
// The return value is zero (MMSYSERR_NOERROR) if this function
|
|
// succeeds with no errors. The return value is a non-zero error code
|
|
// if the function fails.
|
|
//
|
|
// An ACM driver should return MMSYSERR_NOTSUPPORTED if a query type
|
|
// is requested that the driver does not understand. Note that a driver
|
|
// must support both the ACM_STREAMSIZEF_DESTINATION and
|
|
// ACM_STREAMSIZEF_SOURCE queries.
|
|
//
|
|
// If the conversion would be 'out of range' given the input arguments,
|
|
// then ACMERR_NOTPOSSIBLE should be returned.
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdStreamSize
|
|
(
|
|
PCODECINST pci,
|
|
LPACMDRVSTREAMINSTANCE padsi,
|
|
LPACMDRVSTREAMSIZE padss
|
|
)
|
|
{
|
|
LPWAVEFORMATEX pwfxSrc;
|
|
LPWAVEFORMATEX pwfxDst;
|
|
LPADPCMWAVEFORMAT pwfadpcm;
|
|
DWORD cb;
|
|
DWORD cBlocks;
|
|
DWORD cbBytesPerBlock;
|
|
|
|
|
|
pwfxSrc = padsi->pwfxSrc;
|
|
pwfxDst = padsi->pwfxDst;
|
|
|
|
|
|
//
|
|
//
|
|
//
|
|
switch (ACM_STREAMSIZEF_QUERYMASK & padss->fdwSize)
|
|
{
|
|
case ACM_STREAMSIZEF_SOURCE:
|
|
cb = padss->cbSrcLength;
|
|
|
|
if (WAVE_FORMAT_ADPCM == pwfxSrc->wFormatTag)
|
|
{
|
|
//
|
|
// how many destination PCM bytes are needed to hold
|
|
// the decoded ADPCM data of padss->cbSrcLength bytes
|
|
//
|
|
// always round UP
|
|
//
|
|
cBlocks = cb / pwfxSrc->nBlockAlign;
|
|
if (0 == cBlocks)
|
|
{
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
pwfadpcm = (LPADPCMWAVEFORMAT)pwfxSrc;
|
|
|
|
cbBytesPerBlock = pwfadpcm->wSamplesPerBlock * pwfxDst->nBlockAlign;
|
|
|
|
if ((0xFFFFFFFFL / cbBytesPerBlock) < cBlocks)
|
|
{
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
if (0 == (cb % pwfxSrc->nBlockAlign))
|
|
{
|
|
cb = cBlocks * cbBytesPerBlock;
|
|
}
|
|
else
|
|
{
|
|
cb = (cBlocks + 1) * cbBytesPerBlock;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// how many destination ADPCM bytes are needed to hold
|
|
// the encoded PCM data of padss->cbSrcLength bytes
|
|
//
|
|
// always round UP
|
|
//
|
|
pwfadpcm = (LPADPCMWAVEFORMAT)pwfxDst;
|
|
|
|
cbBytesPerBlock = pwfadpcm->wSamplesPerBlock * pwfxSrc->nBlockAlign;
|
|
|
|
cBlocks = cb / cbBytesPerBlock;
|
|
|
|
if (0 == (cb % cbBytesPerBlock))
|
|
{
|
|
cb = cBlocks * pwfxDst->nBlockAlign;
|
|
}
|
|
else
|
|
{
|
|
cb = (cBlocks + 1) * pwfxDst->nBlockAlign;
|
|
}
|
|
|
|
if (0L == cb)
|
|
{
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
}
|
|
|
|
padss->cbDstLength = cb;
|
|
return (MMSYSERR_NOERROR);
|
|
|
|
|
|
case ACM_STREAMSIZEF_DESTINATION:
|
|
cb = padss->cbDstLength;
|
|
|
|
if (WAVE_FORMAT_ADPCM == pwfxDst->wFormatTag)
|
|
{
|
|
//
|
|
// how many source PCM bytes can be encoded into a
|
|
// destination buffer of padss->cbDstLength bytes
|
|
//
|
|
// always round DOWN
|
|
//
|
|
cBlocks = cb / pwfxDst->nBlockAlign;
|
|
if (0 == cBlocks)
|
|
{
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
pwfadpcm = (LPADPCMWAVEFORMAT)pwfxDst;
|
|
|
|
cbBytesPerBlock = pwfadpcm->wSamplesPerBlock * pwfxSrc->nBlockAlign;
|
|
|
|
if ((0xFFFFFFFFL / cbBytesPerBlock) < cBlocks)
|
|
{
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
cb = cBlocks * cbBytesPerBlock;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// how many source ADPCM bytes can be decoded into a
|
|
// destination buffer of padss->cbDstLength bytes
|
|
//
|
|
// always round DOWN
|
|
//
|
|
pwfadpcm = (LPADPCMWAVEFORMAT)pwfxSrc;
|
|
|
|
cbBytesPerBlock = pwfadpcm->wSamplesPerBlock * pwfxDst->nBlockAlign;
|
|
|
|
cBlocks = cb / cbBytesPerBlock;
|
|
if (0 == cBlocks)
|
|
{
|
|
return (ACMERR_NOTPOSSIBLE);
|
|
}
|
|
|
|
cb = cBlocks * pwfxSrc->nBlockAlign;
|
|
}
|
|
|
|
padss->cbSrcLength = cb;
|
|
return (MMSYSERR_NOERROR);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
return (MMSYSERR_NOTSUPPORTED);
|
|
} // acmdStreamSize()
|
|
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT acmdStreamConvert
|
|
//
|
|
// Description:
|
|
// This function handles the ACMDM_STREAM_CONVERT message. This is the
|
|
// whole purpose of writing a codec--to convert data. This message is
|
|
// sent after a stream has been opened (the codec receives and succeeds
|
|
// the ACMDM_STREAM_OPEN message).
|
|
//
|
|
// Arguments:
|
|
// PCODECINST pci: Pointer to private codec instance structure.
|
|
//
|
|
// LPACMDRVSTREAMHEADER pdsh: Pointer to a conversion stream instance
|
|
// structure.
|
|
//
|
|
// DWORD fdwConvert: Misc. flags for how conversion should be done.
|
|
//
|
|
// Return (LRESULT):
|
|
// The return value is zero (MMSYSERR_NOERROR) if this function
|
|
// succeeds with no errors. The return value is a non-zero ACMERR_*
|
|
// or MMSYSERR_* if the function fails.
|
|
//
|
|
// History:
|
|
// 11/28/92 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNLOCAL acmdStreamConvert
|
|
(
|
|
PCODECINST pci,
|
|
LPACMDRVSTREAMINSTANCE padsi,
|
|
LPACMDRVSTREAMHEADER padsh
|
|
)
|
|
{
|
|
CONVERTPROC_C fpConvertC;
|
|
#ifndef WIN32
|
|
CONVERTPROC_ASM fpConvertAsm;
|
|
BOOL fRealTime;
|
|
#endif
|
|
BOOL fBlockAlign;
|
|
BOOL fDecode;
|
|
LPWAVEFORMATEX pwfpcm;
|
|
LPADPCMWAVEFORMAT pwfadpcm;
|
|
DWORD dw;
|
|
|
|
fBlockAlign = (0 != (ACM_STREAMCONVERTF_BLOCKALIGN & padsh->fdwConvert));
|
|
fDecode = ( WAVE_FORMAT_PCM == padsi->pwfxDst->wFormatTag );
|
|
|
|
if( !fDecode )
|
|
{
|
|
//
|
|
// encode
|
|
//
|
|
pwfpcm = padsi->pwfxSrc;
|
|
pwfadpcm = (LPADPCMWAVEFORMAT)padsi->pwfxDst;
|
|
|
|
dw = PCM_BYTESTOSAMPLES(pwfpcm, padsh->cbSrcLength);
|
|
|
|
if (fBlockAlign)
|
|
{
|
|
dw = (dw / pwfadpcm->wSamplesPerBlock) * pwfadpcm->wSamplesPerBlock;
|
|
}
|
|
|
|
//
|
|
// Look for an easy exit. We can only handle an even number of
|
|
// samples.
|
|
//
|
|
if( dw < 2 )
|
|
{
|
|
padsh->cbDstLengthUsed = 0;
|
|
|
|
if( fBlockAlign )
|
|
padsh->cbSrcLengthUsed = 0;
|
|
else
|
|
padsh->cbSrcLengthUsed = padsh->cbSrcLength;
|
|
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
//
|
|
// Make sure we have an even number of samples.
|
|
//
|
|
dw &= ~1;
|
|
|
|
|
|
dw = PCM_SAMPLESTOBYTES(pwfpcm, dw);
|
|
|
|
padsh->cbSrcLengthUsed = dw;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Decode.
|
|
//
|
|
|
|
pwfadpcm = (LPADPCMWAVEFORMAT)padsi->pwfxSrc;
|
|
pwfpcm = padsi->pwfxDst;
|
|
|
|
//
|
|
// Determine the number of samples to convert.
|
|
//
|
|
dw = padsh->cbSrcLength;
|
|
if (fBlockAlign) {
|
|
dw = (dw / pwfadpcm->wfx.nBlockAlign) * pwfadpcm->wfx.nBlockAlign;
|
|
}
|
|
padsh->cbSrcLengthUsed = dw;
|
|
}
|
|
|
|
//
|
|
// Call the conversion routine.
|
|
//
|
|
#ifdef WIN32
|
|
|
|
fpConvertC = (CONVERTPROC_C)padsi->dwDriver;
|
|
padsh->cbDstLengthUsed = (*fpConvertC)(
|
|
(HPBYTE)padsh->pbSrc,
|
|
padsh->cbSrcLengthUsed,
|
|
(HPBYTE)padsh->pbDst,
|
|
(UINT)pwfadpcm->wfx.nBlockAlign,
|
|
(UINT)pwfadpcm->wSamplesPerBlock,
|
|
(UINT)pwfadpcm->wNumCoef,
|
|
(LPADPCMCOEFSET)&(pwfadpcm->aCoef[0])
|
|
);
|
|
|
|
#else
|
|
|
|
fRealTime = (0L == (padsi->fdwOpen & ACM_STREAMOPENF_NONREALTIME) );
|
|
if( fDecode || fRealTime ) {
|
|
fpConvertAsm = (CONVERTPROC_ASM)padsi->dwDriver;
|
|
padsh->cbDstLengthUsed = (*fpConvertAsm)(
|
|
padsi->pwfxSrc,
|
|
padsh->pbSrc,
|
|
padsi->pwfxDst,
|
|
padsh->pbDst,
|
|
padsh->cbSrcLengthUsed
|
|
);
|
|
} else {
|
|
fpConvertC = (CONVERTPROC_C)padsi->dwDriver;
|
|
padsh->cbDstLengthUsed = (*fpConvertC)(
|
|
(HPBYTE)padsh->pbSrc,
|
|
padsh->cbSrcLengthUsed,
|
|
(HPBYTE)padsh->pbDst,
|
|
(UINT)pwfadpcm->wfx.nBlockAlign,
|
|
(UINT)pwfadpcm->wSamplesPerBlock,
|
|
(UINT)pwfadpcm->wNumCoef,
|
|
(LPADPCMCOEFSET)&(pwfadpcm->aCoef[0])
|
|
);
|
|
}
|
|
|
|
#endif
|
|
|
|
return (MMSYSERR_NOERROR);
|
|
} // acmdStreamConvert()
|
|
|
|
|
|
//==========================================================================;
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================;
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// LRESULT DriverProc
|
|
//
|
|
// Description:
|
|
//
|
|
//
|
|
// Arguments:
|
|
// DWORD_PTR dwId: For most messages, dwId is the DWORD_PTR value that
|
|
// the driver returns in response to a DRV_OPEN message. Each time
|
|
// that the driver is opened, through the DrvOpen API, the driver
|
|
// receives a DRV_OPEN message and can return an arbitrary, non-zero
|
|
// value. The installable driver interface saves this value and returns
|
|
// a unique driver handle to the application. Whenever the application
|
|
// sends a message to the driver using the driver handle, the interface
|
|
// routes the message to this entry point and passes the corresponding
|
|
// dwId. This mechanism allows the driver to use the same or different
|
|
// identifiers for multiple opens but ensures that driver handles are
|
|
// unique at the application interface layer.
|
|
//
|
|
// The following messages are not related to a particular open instance
|
|
// of the driver. For these messages, the dwId will always be zero.
|
|
//
|
|
// DRV_LOAD, DRV_FREE, DRV_ENABLE, DRV_DISABLE, DRV_OPEN
|
|
//
|
|
// HDRVR hdrvr: This is the handle returned to the application
|
|
// by the driver interface.
|
|
//
|
|
// UINT uMsg: The requested action to be performed. Message
|
|
// values below DRV_RESERVED are used for globally defined messages.
|
|
// Message values from DRV_RESERVED to DRV_USER are used for defined
|
|
// driver protocols. Messages above DRV_USER are used for driver
|
|
// specific messages.
|
|
//
|
|
// LPARAM lParam1: Data for this message. Defined separately for
|
|
// each message.
|
|
//
|
|
// LPARAM lParam2: Data for this message. Defined separately for
|
|
// each message.
|
|
//
|
|
//
|
|
// Return (LRESULT):
|
|
// Defined separately for each message.
|
|
//
|
|
// History:
|
|
// 11/16/92 cjp [curtisp]
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
LRESULT FNEXPORT DriverProc
|
|
(
|
|
DWORD_PTR dwId,
|
|
HDRVR hdrvr,
|
|
UINT uMsg,
|
|
LPARAM lParam1,
|
|
LPARAM lParam2
|
|
)
|
|
{
|
|
LRESULT lr;
|
|
PCODECINST pci;
|
|
|
|
//
|
|
// make pci either NULL or a valid instance pointer. note that dwId
|
|
// is 0 for several of the DRV_* messages (ie DRV_LOAD, DRV_OPEN...)
|
|
// see acmdDriverOpen for information on what dwId is for other
|
|
// messages (instance data).
|
|
//
|
|
pci = (PCODECINST)dwId;
|
|
|
|
switch (uMsg)
|
|
{
|
|
//
|
|
// lParam1: Unused.
|
|
//
|
|
// lParam2: Unused.
|
|
//
|
|
case DRV_LOAD:
|
|
#ifdef WIN32
|
|
DbgInitialize(TRUE);
|
|
#endif
|
|
DPF(4, "DRV_LOAD");
|
|
return(1L);
|
|
|
|
//
|
|
// lParam1: Unused.
|
|
//
|
|
// lParam2: Unused.
|
|
//
|
|
case DRV_FREE:
|
|
DPF(4, "DRV_FREE");
|
|
return (1L);
|
|
|
|
//
|
|
// lParam1: Not used. Ignore this argument.
|
|
//
|
|
// lParam2: Pointer to ACMDRVOPENDESC (or NULL).
|
|
//
|
|
case DRV_OPEN:
|
|
DPF(4, "DRV_OPEN");
|
|
lr = acmdDriverOpen(hdrvr, (LPACMDRVOPENDESC)lParam2);
|
|
return (lr);
|
|
|
|
//
|
|
// lParam1: Unused.
|
|
//
|
|
// lParam2: Unused.
|
|
//
|
|
case DRV_CLOSE:
|
|
DPF(4, "DRV_CLOSE");
|
|
lr = acmdDriverClose(pci);
|
|
return (lr);
|
|
|
|
//
|
|
// lParam1: Unused.
|
|
//
|
|
// lParam2: Unused.
|
|
//
|
|
case DRV_INSTALL:
|
|
DPF(4, "DRV_INSTALL");
|
|
return ((LRESULT)DRVCNF_RESTART);
|
|
|
|
//
|
|
// lParam1: Unused.
|
|
//
|
|
// lParam2: Unused.
|
|
//
|
|
case DRV_REMOVE:
|
|
DPF(4, "DRV_REMOVE");
|
|
return ((LRESULT)DRVCNF_RESTART);
|
|
|
|
|
|
|
|
//
|
|
// lParam1: Not used.
|
|
//
|
|
// lParam2: Not used.
|
|
//
|
|
case DRV_QUERYCONFIGURE:
|
|
DPF(4, "DRV_QUERYCONFIGURE");
|
|
//
|
|
// set up lParam1 and lParam2 to values that can be used by
|
|
// acmdDriverConfigure to know that it is being 'queried'
|
|
// for hardware configuration support.
|
|
//
|
|
lParam1 = -1L;
|
|
lParam2 = 0L;
|
|
|
|
//--fall through--//
|
|
|
|
//
|
|
// lParam1: Handle to parent window for the configuration dialog
|
|
// box.
|
|
//
|
|
// lParam2: Optional pointer to DRVCONFIGINFO structure.
|
|
//
|
|
case DRV_CONFIGURE:
|
|
DPF(4, "DRV_CONFIGURE");
|
|
lr = acmdDriverConfigure(pci, (HWND)lParam1,
|
|
(LPDRVCONFIGINFO)lParam2);
|
|
return (lr);
|
|
|
|
|
|
//
|
|
// lParam1: Pointer to ACMDRIVERDETAILS structure.
|
|
//
|
|
// lParam2: Size in bytes of ACMDRIVERDETAILS stucture passed.
|
|
//
|
|
case ACMDM_DRIVER_DETAILS:
|
|
DPF(4, "ACMDM_DRIVER_DETAILS");
|
|
lr = acmdDriverDetails(pci, (LPACMDRIVERDETAILS)lParam1);
|
|
return (lr);
|
|
|
|
//
|
|
// lParam1: Handle to parent window to use if displaying your own
|
|
// about box.
|
|
//
|
|
// lParam2: Not used.
|
|
//
|
|
case ACMDM_DRIVER_ABOUT:
|
|
DPF(4, "ACMDM_DRIVER_ABOUT");
|
|
lr = acmdDriverAbout(pci, (HWND)lParam1);
|
|
return (lr);
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//--------------------------------------------------------------------------;
|
|
|
|
//
|
|
// lParam1: Pointer to ACMDRVFORMATSUGGEST structure.
|
|
//
|
|
// lParam2: Not used.
|
|
//
|
|
case ACMDM_FORMAT_SUGGEST:
|
|
DPF(4, "ACMDM_FORMAT_SUGGEST");
|
|
lr = acmdFormatSuggest(pci, (LPACMDRVFORMATSUGGEST)lParam1 );
|
|
return (lr);
|
|
|
|
|
|
//
|
|
// lParam1: FORMATTAGDETAILS
|
|
//
|
|
// lParam2: Not used.
|
|
//
|
|
case ACMDM_FORMATTAG_DETAILS:
|
|
DPF(4, "ACMDM_FORMATTAG_DETAILS");
|
|
lr = acmdFormatTagDetails(pci, (LPACMFORMATTAGDETAILS)lParam1, (DWORD)lParam2);
|
|
return (lr);
|
|
|
|
//
|
|
// lParam1: FORMATDETAILS
|
|
//
|
|
// lParam2: fdwDetails
|
|
//
|
|
case ACMDM_FORMAT_DETAILS:
|
|
DPF(4, "ACMDM_FORMAT_DETAILS");
|
|
lr = acmdFormatDetails(pci, (LPACMFORMATDETAILS)lParam1, (DWORD)lParam2);
|
|
return (lr);
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//--------------------------------------------------------------------------;
|
|
|
|
//
|
|
// lParam1: Pointer to ACMDRVSTREAMINSTANCE structure.
|
|
//
|
|
// lParam2: Not used.
|
|
//
|
|
case ACMDM_STREAM_OPEN:
|
|
DPF(4, "ACMDM_STREAM_OPEN");
|
|
lr = acmdStreamOpen(pci, (LPACMDRVSTREAMINSTANCE)lParam1);
|
|
return (lr);
|
|
|
|
//
|
|
// lParam1: Pointer to ACMDRVSTREAMINSTANCE structure.
|
|
//
|
|
// lParam2: Not Used.
|
|
//
|
|
case ACMDM_STREAM_CLOSE:
|
|
DPF(4, "ACMDM_STREAM_CLOSE");
|
|
lr = acmdStreamClose(pci, (LPACMDRVSTREAMINSTANCE)lParam1);
|
|
return (lr);
|
|
|
|
//
|
|
// lParam1: Pointer to ACMDRVSTREAMINSTANCE structure.
|
|
//
|
|
// lParam2: Pointer to ACMDRVSTREAMSIZE structure.
|
|
//
|
|
case ACMDM_STREAM_SIZE:
|
|
DPF(4, "ACMDM_STREAM_SIZE");
|
|
lr = acmdStreamSize(pci, (LPACMDRVSTREAMINSTANCE)lParam1, (LPACMDRVSTREAMSIZE)lParam2);
|
|
return (lr);
|
|
|
|
//
|
|
// lParam1: Pointer to ACMDRVSTREAMINSTANCE structure.
|
|
//
|
|
// lParam2: Pointer to ACMDRVSTREAMHEADER structure.
|
|
//
|
|
case ACMDM_STREAM_CONVERT:
|
|
DPF(4, "ACMDM_STREAM_CONVERT");
|
|
lr = acmdStreamConvert(pci, (LPACMDRVSTREAMINSTANCE)lParam1, (LPACMDRVSTREAMHEADER)lParam2);
|
|
return (lr);
|
|
}
|
|
|
|
//
|
|
// if we are executing the following code, then this codec does not
|
|
// handle the message that was sent. there are two ranges of messages
|
|
// we need to deal with:
|
|
//
|
|
// o ACM specific driver messages: if a codec does not answer a
|
|
// message sent in the ACM driver message range, then it must
|
|
// return MMSYSERR_NOTSUPPORTED. this applies to the 'user'
|
|
// range as well (for consistency).
|
|
//
|
|
// o Other installable driver messages: if a codec does not answer
|
|
// a message that is NOT in the ACM driver message range, then
|
|
// it must call DefDriverProc and return that result.
|
|
//
|
|
DPF(4, "OTHER MESSAGE RECEIVED BY DRIVERPROC");
|
|
if (uMsg >= ACMDM_USER)
|
|
return (MMSYSERR_NOTSUPPORTED);
|
|
else
|
|
return (DefDriverProc(dwId, hdrvr, uMsg, lParam1, lParam2));
|
|
} // DriverProc()
|