📄 msadp32.c
字号:
/*
* MS ADPCM handling
*
* Copyright (C) 2002 Eric Pouech
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <assert.h>
#include <stdarg.h>
#include <string.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "mmsystem.h"
#include "mmreg.h"
#include "msacm.h"
#include "msacmdrv.h"
#include "wine/debug.h"
/* see http://www.pcisys.net/~melanson/codecs/adpcm.txt for the details */
WINE_DEFAULT_DEBUG_CHANNEL(adpcm);
/***********************************************************************
* ADPCM_drvOpen
*/
static DWORD ADPCM_drvOpen(LPCSTR str)
{
return 1;
}
/***********************************************************************
* ADPCM_drvClose
*/
static DWORD ADPCM_drvClose(DWORD dwDevID)
{
return 1;
}
typedef struct tagAcmAdpcmData
{
void (*convert)(PACMDRVSTREAMINSTANCE adsi,
const unsigned char*, LPDWORD, unsigned char*, LPDWORD);
} AcmAdpcmData;
/* table to list all supported formats... those are the basic ones. this
* also helps given a unique index to each of the supported formats
*/
typedef struct
{
int nChannels;
int nBits;
int rate;
} Format;
static Format PCM_Formats[] =
{
{1, 8, 8000}, {2, 8, 8000}, {1, 16, 8000}, {2, 16, 8000},
{1, 8, 11025}, {2, 8, 11025}, {1, 16, 11025}, {2, 16, 11025},
{1, 8, 22050}, {2, 8, 22050}, {1, 16, 22050}, {2, 16, 22050},
{1, 8, 44100}, {2, 8, 44100}, {1, 16, 44100}, {2, 16, 44100},
};
static Format ADPCM_Formats[] =
{
{1, 4, 8000}, {2, 4, 8000}, {1, 4, 11025}, {2, 4, 11025},
{1, 4, 22050}, {2, 4, 22050}, {1, 4, 44100}, {2, 4, 44100},
};
#define NUM_PCM_FORMATS (sizeof(PCM_Formats) / sizeof(PCM_Formats[0]))
#define NUM_ADPCM_FORMATS (sizeof(ADPCM_Formats) / sizeof(ADPCM_Formats[0]))
static int MS_Delta[] =
{
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
};
static ADPCMCOEFSET MSADPCM_CoeffSet[] =
{
{256, 0}, {512, -256}, {0, 0}, {192, 64}, {240, 0}, {460, -208}, {392, -232}
};
/***********************************************************************
* ADPCM_GetFormatIndex
*/
static DWORD ADPCM_GetFormatIndex(WAVEFORMATEX* wfx)
{
int i, hi;
Format* fmts;
switch (wfx->wFormatTag)
{
case WAVE_FORMAT_PCM:
hi = NUM_PCM_FORMATS;
fmts = PCM_Formats;
break;
case WAVE_FORMAT_ADPCM:
hi = NUM_ADPCM_FORMATS;
fmts = ADPCM_Formats;
break;
default:
return 0xFFFFFFFF;
}
for (i = 0; i < hi; i++)
{
if (wfx->nChannels == fmts[i].nChannels &&
wfx->nSamplesPerSec == fmts[i].rate &&
wfx->wBitsPerSample == fmts[i].nBits)
return i;
}
return 0xFFFFFFFF;
}
static void init_wfx_adpcm(ADPCMWAVEFORMAT* awfx)
{
register WAVEFORMATEX* pwfx = &awfx->wfx;
/* we assume wFormatTag, nChannels, nSamplesPerSec and wBitsPerSample
* have been initialized... */
if (pwfx->wFormatTag != WAVE_FORMAT_ADPCM) {FIXME("wrong FT\n"); return;}
if (ADPCM_GetFormatIndex(pwfx) == 0xFFFFFFFF) {FIXME("wrong fmt\n"); return;}
switch (pwfx->nSamplesPerSec)
{
case 8000: pwfx->nBlockAlign = 256; break;
case 11025: pwfx->nBlockAlign = 256; break;
case 22050: pwfx->nBlockAlign = 512; break;
default:
case 44100: pwfx->nBlockAlign = 1024; break;
}
pwfx->cbSize = 2 * sizeof(WORD) + 7 * sizeof(ADPCMCOEFSET);
/* 7 is the size of the block head (which contains two samples) */
awfx->wSamplesPerBlock = (pwfx->nBlockAlign - (7 * pwfx->nChannels)) * (2 / pwfx->nChannels) + 2;
pwfx->nAvgBytesPerSec = (pwfx->nSamplesPerSec * pwfx->nBlockAlign) / awfx->wSamplesPerBlock;
awfx->wNumCoef = 7;
memcpy(awfx->aCoef, MSADPCM_CoeffSet, 7 * sizeof(ADPCMCOEFSET));
}
/***********************************************************************
* R16
*
* Read a 16 bit sample (correctly handles endianess)
*/
static inline short R16(const unsigned char* src)
{
return (short)((unsigned short)src[0] | ((unsigned short)src[1] << 8));
}
/***********************************************************************
* W16
*
* Write a 16 bit sample (correctly handles endianess)
*/
static inline void W16(unsigned char* dst, short s)
{
dst[0] = LOBYTE(s);
dst[1] = HIBYTE(s);
}
static inline void clamp_sample(int* sample)
{
if (*sample < -32768) *sample = -32768;
if (*sample > 32767) *sample = 32767;
}
static inline void process_nibble(unsigned nibble, int* idelta,
int* sample1, int* sample2,
const ADPCMCOEFSET* coeff)
{
int sample;
int snibble;
/* nibble is in fact a signed 4 bit integer => propagate sign if needed */
snibble = (nibble & 0x08) ? (nibble - 16) : nibble;
sample = ((*sample1 * coeff->iCoef1) + (*sample2 * coeff->iCoef2)) / 256 +
snibble * *idelta;
clamp_sample(&sample);
*sample2 = *sample1;
*sample1 = sample;
*idelta = ((MS_Delta[nibble] * *idelta) / 256);
if (*idelta < 16) *idelta = 16;
}
static void cvtSSms16K(PACMDRVSTREAMINSTANCE adsi,
const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
int ideltaL, ideltaR;
int sample1L, sample2L;
int sample1R, sample2R;
ADPCMCOEFSET coeffL, coeffR;
int nsamp;
int nsamp_blk = ((ADPCMWAVEFORMAT*)adsi->pwfxSrc)->wSamplesPerBlock;
DWORD nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign,
*ndst / (nsamp_blk * 2 * 2));
*nsrc = nblock * adsi->pwfxSrc->nBlockAlign;
*ndst = nblock * nsamp_blk * 2 * 2;
nsamp_blk -= 2; /* see below for samples from block head */
for (; nblock > 0; nblock--)
{
const unsigned char* in_src = src;
assert(*src <= 6);
coeffL = MSADPCM_CoeffSet[*src++];
assert(*src <= 6);
coeffR = MSADPCM_CoeffSet[*src++];
ideltaL = R16(src); src += 2;
ideltaR = R16(src); src += 2;
sample1L = R16(src); src += 2;
sample1R = R16(src); src += 2;
sample2L = R16(src); src += 2;
sample2R = R16(src); src += 2;
/* store samples from block head */
W16(dst, sample2L); dst += 2;
W16(dst, sample2R); dst += 2;
W16(dst, sample1L); dst += 2;
W16(dst, sample1R); dst += 2;
for (nsamp = nsamp_blk; nsamp > 0; nsamp--)
{
process_nibble(*src >> 4, &ideltaL, &sample1L, &sample2L, &coeffL);
W16(dst, sample1L); dst += 2;
process_nibble(*src++ & 0x0F, &ideltaR, &sample1R, &sample2R, &coeffR);
W16(dst, sample1R); dst += 2;
}
src = in_src + adsi->pwfxSrc->nBlockAlign;
}
}
static void cvtMMms16K(PACMDRVSTREAMINSTANCE adsi,
const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
int idelta;
int sample1, sample2;
ADPCMCOEFSET coeff;
int nsamp;
int nsamp_blk = ((ADPCMWAVEFORMAT*)adsi->pwfxSrc)->wSamplesPerBlock;
DWORD nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign,
*ndst / (nsamp_blk * 2));
*nsrc = nblock * adsi->pwfxSrc->nBlockAlign;
*ndst = nblock * nsamp_blk * 2;
nsamp_blk -= 2; /* see below for samples from block head */
for (; nblock > 0; nblock--)
{
const unsigned char* in_src = src;
assert(*src <= 6);
coeff = MSADPCM_CoeffSet[*src++];
idelta = R16(src); src += 2;
sample1 = R16(src); src += 2;
sample2 = R16(src); src += 2;
/* store samples from block head */
W16(dst, sample2); dst += 2;
W16(dst, sample1); dst += 2;
for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 2)
{
process_nibble(*src >> 4, &idelta, &sample1, &sample2, &coeff);
W16(dst, sample1); dst += 2;
process_nibble(*src++ & 0x0F, &idelta, &sample1, &sample2, &coeff);
W16(dst, sample1); dst += 2;
}
src = in_src + adsi->pwfxSrc->nBlockAlign;
}
}
#if 0
static void cvtSS16msK(PACMDRVSTREAMINSTANCE adsi,
const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
}
static void cvtMM16msK(PACMDRVSTREAMINSTANCE adsi,
const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
}
#endif
/***********************************************************************
* ADPCM_DriverDetails
*
*/
static LRESULT ADPCM_DriverDetails(PACMDRIVERDETAILSW add)
{
add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
add->fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
add->wMid = 0xFF;
add->wPid = 0x00;
add->vdwACM = 0x01000000;
add->vdwDriver = 0x01000000;
add->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
add->cFormatTags = 2; /* PCM, MS ADPCM */
add->cFilterTags = 0;
add->hicon = NULL;
MultiByteToWideChar( CP_ACP, 0, "WINE-MS ADPCM", -1,
add->szShortName, sizeof(add->szShortName)/sizeof(WCHAR) );
MultiByteToWideChar( CP_ACP, 0, "Wine MS ADPCM converter", -1,
add->szLongName, sizeof(add->szLongName)/sizeof(WCHAR) );
MultiByteToWideChar( CP_ACP, 0, "Brought to you by the Wine team...", -1,
add->szCopyright, sizeof(add->szCopyright)/sizeof(WCHAR) );
MultiByteToWideChar( CP_ACP, 0, "Refer to LICENSE file", -1,
add->szLicensing, sizeof(add->szLicensing)/sizeof(WCHAR) );
add->szFeatures[0] = 0;
return MMSYSERR_NOERROR;
}
/***********************************************************************
* ADPCM_FormatTagDetails
*
*/
static LRESULT ADPCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery)
{
static WCHAR szPcm[]={'P','C','M',0};
static WCHAR szMsAdPcm[]={'M','S',' ','A','d','P','C','M',0};
switch (dwQuery)
{
case ACM_FORMATTAGDETAILSF_INDEX:
if (aftd->dwFormatTagIndex >= 2) return ACMERR_NOTPOSSIBLE;
break;
case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
if (aftd->dwFormatTag == WAVE_FORMAT_UNKNOWN)
{
aftd->dwFormatTagIndex = 1; /* WAVE_FORMAT_ADPCM is bigger than PCM */
break;
}
/* fall thru */
case ACM_FORMATTAGDETAILSF_FORMATTAG:
switch (aftd->dwFormatTag)
{
case WAVE_FORMAT_PCM: aftd->dwFormatTagIndex = 0; break;
case WAVE_FORMAT_ADPCM: aftd->dwFormatTagIndex = 1; break;
default: return ACMERR_NOTPOSSIBLE;
}
break;
default:
WARN("Unsupported query %08lx\n", dwQuery);
return MMSYSERR_NOTSUPPORTED;
}
aftd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
switch (aftd->dwFormatTagIndex)
{
case 0:
aftd->dwFormatTag = WAVE_FORMAT_PCM;
aftd->cbFormatSize = sizeof(PCMWAVEFORMAT);
aftd->cStandardFormats = NUM_PCM_FORMATS;
lstrcpyW(aftd->szFormatTag, szPcm);
break;
case 1:
aftd->dwFormatTag = WAVE_FORMAT_ADPCM;
aftd->cbFormatSize = sizeof(ADPCMWAVEFORMAT) + (7 - 1) * sizeof(ADPCMCOEFSET);
aftd->cStandardFormats = NUM_ADPCM_FORMATS;
lstrcpyW(aftd->szFormatTag, szMsAdPcm);
break;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -