📄 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 0static 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 + -