📄 gsm610.c
字号:
//==========================================================================;
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1993-1999 Microsoft Corporation
//
//--------------------------------------------------------------------------;
//
// gsm610.c
//
// Description:
// This file contains encode and decode routines for the
// GSM 06.10 standard.
//
//==========================================================================;
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <mmreg.h>
#include <msacm.h>
#include <msacmdrv.h>
#include "codec.h"
#include "gsm610.h"
#include "debug.h"
typedef BYTE HUGE *HPBYTE;
#ifdef WIN32
typedef WORD UNALIGNED *HPWORD;
#else
typedef WORD HUGE *HPWORD;
#endif
//**************************************************************************
/*
This source module has the following structure.
Section 1:
Highest level functions. These functions are called from outside
this module.
Section 2:
Encoding support functions. These functions support
the encoding process.
Section 3:
Decoding support functions. These functions support
the decoding process.
Section 4:
Math functions used by any of the above functions.
Most of the encode and decode support routines are direct implementations of
the pseudocode algorithms described in the GSM 6.10 specification. Some
changes were made where necessary or where optimization was obvious or
necessary.
Most variables are named as in the GSM 6.10 spec, departing from the common
hungarian notation. This facilitates referencing the specification when
studying this implementation.
Some of the functions are conditionally compiled per the definition of
the WIN32 and _X86_ symbol. These functions have analogous alternate
implementations in 80386 assembler (in GSM61016.ASM and GSM61032.ASM) for
the purposes of execution speed. The 'C' implementations of these functions
are left intact for portability and can also be referenced when studying the
assembler implementations. Symbols accessed in/from GSM610xx.ASM are
declared with the EXTERN_C linkage macro.
*/
//**************************************************************************
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//
// Typedefs
//
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
#ifndef LPSHORT
typedef SHORT FAR *LPSHORT;
#endif
//
// XM is an RPE sequence containing 13 samples. There is one
// RPE sequence per sub-frame. This is typedefed in order to
// facilitate passing the array thru function calls.
//
typedef SHORT XM[13];
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//
// Macros
//
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
#define BITSHIFTLEFT(x,c) ( ((c)>=0) ? ((x)<<(c)) : ((x)>>(-(c))) )
#define BITSHIFTRIGHT(x,c) ( ((c)>=0) ? ((x)>>(c)) : ((x)<<(-(c))) )
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//
// function protos
//
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//
//
// Math function protos
//
__inline SHORT add(SHORT var1, SHORT var2);
__inline SHORT sub(SHORT var1, SHORT var2);
__inline SHORT mult(SHORT var1, SHORT var2);
__inline SHORT mult_r(SHORT var1, SHORT var2);
__inline SHORT gabs(SHORT var1);
__inline SHORT gdiv(SHORT var1, SHORT var2);
__inline LONG l_mult(SHORT var1, SHORT var2);
__inline LONG l_add(LONG l_var1, LONG l_var2);
__inline LONG l_sub(LONG l_var1, LONG l_var2);
__inline SHORT norm(LONG l_var1);
__inline LONG IsNeg(LONG x);
//
// helper functions
//
__inline SHORT Convert8To16BitPCM(BYTE);
__inline BYTE Convert16To8BitPCM(SHORT);
//
//
// encode functions
//
void encodePreproc
( PSTREAMINSTANCE psi,
LPSHORT sop,
LPSHORT s );
void encodeLPCAnalysis
( PSTREAMINSTANCE psi,
LPSHORT s,
LPSHORT LARc );
void encodeLPCFilter
( PSTREAMINSTANCE psi,
LPSHORT LARc,
LPSHORT s,
LPSHORT d );
EXTERN_C void encodeLTPAnalysis
( PSTREAMINSTANCE psi,
LPSHORT d,
LPSHORT pNc,
LPSHORT pbc );
void encodeLTPFilter
( PSTREAMINSTANCE psi,
SHORT bc,
SHORT Nc,
LPSHORT d,
LPSHORT e,
LPSHORT dpp );
void encodeRPE
( PSTREAMINSTANCE psi,
LPSHORT e,
LPSHORT pMc,
LPSHORT pxmaxc,
LPSHORT xMc,
LPSHORT ep );
void encodeUpdate
( PSTREAMINSTANCE psi,
LPSHORT ep,
LPSHORT dpp );
void PackFrame0
( BYTE FAR ab[],
SHORT FAR LAR[],
SHORT FAR N[],
SHORT FAR b[],
SHORT FAR M[],
SHORT FAR Xmax[],
XM FAR X[] );
void PackFrame1
( BYTE FAR ab[],
SHORT FAR LAR[],
SHORT FAR N[],
SHORT FAR b[],
SHORT FAR M[],
SHORT FAR Xmax[],
XM FAR X[] );
//
//
// decode functions
//
void decodeRPE
( PSTREAMINSTANCE psi,
SHORT Mcr,
SHORT xmaxcr,
LPSHORT xMcr,
LPSHORT erp );
EXTERN_C void decodeLTP
( PSTREAMINSTANCE psi,
SHORT bcr,
SHORT Ncr,
LPSHORT erp );
void decodeLPC
( PSTREAMINSTANCE psi,
LPSHORT LARcr,
LPSHORT wt,
LPSHORT sr );
EXTERN_C void decodePostproc
( PSTREAMINSTANCE psi,
LPSHORT sr,
LPSHORT srop );
void UnpackFrame0
( BYTE FAR ab[],
SHORT FAR LAR[],
SHORT FAR N[],
SHORT FAR b[],
SHORT FAR M[],
SHORT FAR Xmax[],
XM FAR X[] );
void UnpackFrame1
( BYTE FAR ab[],
SHORT FAR LAR[],
SHORT FAR N[],
SHORT FAR b[],
SHORT FAR M[],
SHORT FAR Xmax[],
XM FAR X[] );
//---------------------------------------------------------------------
//---------------------------------------------------------------------
//
// Functions
//
//---------------------------------------------------------------------
//---------------------------------------------------------------------
//---------------------------------------------------------------------
//
// gsm610Reset(PSTREAMINSTANCE psi)
//
// Description:
// Resets the gsm610-specific stream instance data for
// the encode/decode routines
//
// Arguments:
// PSTREAMINSTANCE psi
// Pointer to stream instance structure
//
// Return value:
// void
// No return value
//
//---------------------------------------------------------------------
void FNGLOBAL gsm610Reset(PSTREAMINSTANCE psi)
{
// For our gsm610 codec, almost all our instance data resets to 0
UINT i;
for (i=0; i<SIZEOF_ARRAY(psi->dp); i++) psi->dp[i] = 0;
for (i=0; i<SIZEOF_ARRAY(psi->drp); i++) psi->drp[i] = 0;
psi->z1 = 0;
psi->l_z2 = 0;
psi->mp = 0;
for (i=0; i<SIZEOF_ARRAY(psi->OldLARpp); i++) psi->OldLARpp[i] = 0;
for (i=0; i<SIZEOF_ARRAY(psi->u); i++) psi->u[i] = 0;
psi->nrp = 40; // The only non-zero init
for (i=0; i<SIZEOF_ARRAY(psi->OldLARrpp); i++) psi->OldLARrpp[i] = 0;
psi->msr = 0;
for (i=0; i<SIZEOF_ARRAY(psi->v); i++) psi->v[i] = 0;
return;
}
//--------------------------------------------------------------------------;
//
// LRESULT gsm610Encode
//
// Description:
// This function handles the ACMDM_STREAM_CONVERT message. This is the
// whole purpose of writing an ACM driver--to convert data. This message
// is sent after a stream has been opened (the driver receives and
// succeeds the ACMDM_STREAM_OPEN message).
//
// Arguments:
// 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.
//
// LPACMDRVSTREAMHEADER padsh: Pointer to stream header structure
// that defines the source data and destination buffer to convert.
//
// 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.
//
//--------------------------------------------------------------------------;
LRESULT FNGLOBAL gsm610Encode
(
LPACMDRVSTREAMINSTANCE padsi,
LPACMDRVSTREAMHEADER padsh
)
{
#if (GSM610_FRAMESPERMONOBLOCK != 2)
#error THIS WAS WRITTEN FOR 2 FRAMES PER BLOCK!!!
#endif
#if (GSM610_MAXCHANNELS > 1)
#error THIS WAS WRITTEN FOR MONO ONLY!!!
#endif
PSTREAMINSTANCE psi;
DWORD cbSrcLen;
BOOL fBlockAlign;
DWORD cb;
DWORD dwcSamples; // dw count of samples
DWORD cBlocks;
UINT i;
HPBYTE hpbSrc, hpbDst;
SHORT sop[GSM610_SAMPLESPERFRAME];
SHORT s[GSM610_SAMPLESPERFRAME];
SHORT d[GSM610_SAMPLESPERFRAME];
SHORT e[GSM610_SAMPLESPERSUBFRAME];
SHORT dpp[GSM610_SAMPLESPERSUBFRAME];
SHORT ep[GSM610_SAMPLESPERSUBFRAME];
// The GSM610 stream data:
SHORT LARc[9]; // LARc[1..8] (one array per frame)
SHORT Nc[GSM610_NUMSUBFRAMES]; // Nc (one per sub-frame)
SHORT bc[GSM610_NUMSUBFRAMES]; // bc (one per sub-frame)
SHORT Mc[GSM610_NUMSUBFRAMES]; // Mc (one per sub-frame)
SHORT xmaxc[GSM610_NUMSUBFRAMES]; // Xmaxc (one per sub-frame)
XM xMc[GSM610_NUMSUBFRAMES]; // xMc (one sequence per sub-frame)
// Temp buffer to hold a block (two frames) of packed stream data
BYTE abBlock[ GSM610_BYTESPERMONOBLOCK ];
UINT nFrame;
UINT cSamples;
#ifdef DEBUG
// ProfSetup(1000,0);
// ProfStart();
#endif
psi = (PSTREAMINSTANCE)padsi->dwDriver;
//
// If this is flagged as the first block of a conversion
// then reset the stream instance data.
//
if (0 != (ACM_STREAMCONVERTF_START & padsh->fdwConvert))
{
gsm610Reset(psi);
}
fBlockAlign = (0 != (ACM_STREAMCONVERTF_BLOCKALIGN & padsh->fdwConvert));
//
// -= encode PCM to GSM 6.10 =-
//
//
//
dwcSamples = PCM_BYTESTOSAMPLES(((LPPCMWAVEFORMAT)(padsi->pwfxSrc)), padsh->cbSrcLength);
cBlocks = dwcSamples / GSM610_SAMPLESPERMONOBLOCK;
if (!fBlockAlign)
{
//
// Add on another block to hold the fragment of
// data at the end of our source data.
//
if (0 != dwcSamples % GSM610_SAMPLESPERMONOBLOCK)
cBlocks++;
}
//
//
//
cb = cBlocks * GSM610_BLOCKALIGNMENT(padsi->pwfxDst);
if (cb > padsh->cbDstLength)
{
return (ACMERR_NOTPOSSIBLE);
}
padsh->cbDstLengthUsed = cb;
if (fBlockAlign)
{
dwcSamples = cBlocks * GSM610_SAMPLESPERMONOBLOCK;
cb = PCM_SAMPLESTOBYTES(((LPPCMWAVEFORMAT)(padsi->pwfxSrc)), dwcSamples);
}
else
{
cb = padsh->cbSrcLength;
}
padsh->cbSrcLengthUsed = cb;
//
//
//
cbSrcLen = padsh->cbSrcLengthUsed;
// Setup huge pointers to our src and dst buffers
hpbSrc = (HPBYTE)padsh->pbSrc;
hpbDst = (HPBYTE)padsh->pbDst;
// Loop thru entire source buffer
while (cbSrcLen)
{
// Process source buffer as two full GSM610 frames
for (nFrame=0; nFrame < 2; nFrame++)
{
//
// the src contains 8- or 16-bit PCM. currently we only
// handle mono conversions.
//
//
// we will fill sop[] with one frame of 16-bit PCM samples
//
//
// copy min( cSrcSamplesLeft, GSM610_SAMPLESPERFRAME ) samples
// to array sop[].
//
dwcSamples = PCM_BYTESTOSAMPLES(((LPPCMWAVEFORMAT)(padsi->pwfxSrc)), cbSrcLen);
cSamples = (int) min(dwcSamples, (DWORD) GSM610_SAMPLESPERFRAME);
if (padsi->pwfxSrc->wBitsPerSample == 16)
{
// copy 16-bit samples from hpbSrc to sop
for (i=0; i < cSamples; i++)
{
sop[i] = *( ((HPWORD)hpbSrc)++ );
}
}
else
{
// copy 8-bit samples from hpbSrc to 16-bit samples in sop
for (i=0; i < cSamples; i++)
{
sop[i] = Convert8To16BitPCM(*hpbSrc++);
}
}
cbSrcLen -= PCM_SAMPLESTOBYTES(((LPPCMWAVEFORMAT)(padsi->pwfxSrc)), cSamples);
// fill out sop[] with silence if necessary.
for ( ; i < GSM610_SAMPLESPERFRAME; i++)
{
sop[i] = 0;
}
//
// Encode a frame of data
//
encodePreproc(psi, sop, s);
encodeLPCAnalysis(psi, s, LARc);
encodeLPCFilter(psi, LARc, s, d);
// For each of four sub-frames
for (i=0; i<4; i++)
{
encodeLTPAnalysis(psi, &d[i*40], &Nc[i], &bc[i]);
encodeLTPFilter(psi, bc[i], Nc[i], &d[i*40], e, dpp);
encodeRPE(psi, e, &Mc[i], &xmaxc[i], xMc[i], ep);
encodeUpdate(psi, ep, dpp);
}
//
// Pack the data and store in dst buffer
//
if (nFrame == 0)
PackFrame0(abBlock, LARc, Nc, bc, Mc, xmaxc, xMc);
else
{
PackFrame1(abBlock, LARc, Nc, bc, Mc, xmaxc, xMc);
for (i=0; i<GSM610_BYTESPERMONOBLOCK; i++)
*(hpbDst++) = abBlock[i];
}
} // for (nFrame...
}
#ifdef DEBUG
// ProfStop();
#endif
return (MMSYSERR_NOERROR);
}
//--------------------------------------------------------------------------;
//
// LRESULT gsm610Decode
//
// Description:
// This function handles the ACMDM_STREAM_CONVERT message. This is the
// whole purpose of writing an ACM driver--to convert data. This message
// is sent after a stream has been opened (the driver receives and
// succeeds the ACMDM_STREAM_OPEN message).
//
// Arguments:
// 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.
//
// LPACMDRVSTREAMHEADER padsh: Pointer to stream header structure
// that defines the source data and destination buffer to convert.
//
// 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.
//
//--------------------------------------------------------------------------;
LRESULT FNGLOBAL gsm610Decode
(
LPACMDRVSTREAMINSTANCE padsi,
LPACMDRVSTREAMHEADER padsh
)
{
#if (GSM610_FRAMESPERMONOBLOCK != 2)
#error THIS WAS WRITTEN FOR 2 FRAMES PER BLOCK!!!
#endif
#if (GSM610_MAXCHANNELS > 1)
#error THIS WAS WRITTEN FOR MONO ONLY!!!
#endif
PSTREAMINSTANCE psi;
DWORD cbSrcLen;
BOOL fBlockAlign;
DWORD cb;
DWORD dwcSamples;
DWORD cBlocks;
HPBYTE hpbSrc, hpbDst;
SHORT erp[GSM610_SAMPLESPERSUBFRAME];
SHORT wt[GSM610_SAMPLESPERFRAME];
SHORT sr[GSM610_SAMPLESPERFRAME];
SHORT srop[GSM610_SAMPLESPERFRAME];
// The GSM610 stream data:
SHORT LARcr[9]; // LARc[1..8] (one array per frame)
SHORT Ncr[GSM610_NUMSUBFRAMES]; // Nc (one per sub-frame)
SHORT bcr[GSM610_NUMSUBFRAMES]; // bc (one per sub-frame)
SHORT Mcr[GSM610_NUMSUBFRAMES]; // Mc (one per sub-frame)
SHORT xmaxcr[GSM610_NUMSUBFRAMES]; // Xmaxc (one per sub-frame)
XM xMcr[GSM610_NUMSUBFRAMES]; // xMc (one sequence per sub-frame)
UINT i,j;
UINT nFrame;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -