Skip to content
Snippets Groups Projects
Commit 3291a058 authored by Eric Pouech's avatar Eric Pouech Committed by Alexandre Julliard
Browse files

Added more conversion routines (rate conversion is implemented).

Cleaned up the code.
parent 9039421b
No related branches found
No related tags found
No related merge requests found
......@@ -4,6 +4,15 @@
* MSACM32 library
*
* Copyright 2000 Eric Pouech
*
* FIXME / TODO list
* + most of the computation should be done in fixed point arithmetic
* instead of floating point (16 bits for integral part, and 16 bits
* for fractional part for example)
* + implement PCM_FormatSuggest function
* + get rid of hack for PCM_DriverProc (msacm32.dll shouldn't export
* a DriverProc, but this would require implementing a generic
* embedded driver handling scheme in msacm32.dll which isn't done yet
*/
#include <assert.h>
......@@ -17,41 +26,65 @@
DEFAULT_DEBUG_CHANNEL(msacm);
/***********************************************************************
* PCM_drvOpen
*/
static DWORD PCM_drvOpen(LPCSTR str)
{
return 1;
}
/***********************************************************************
* PCM_drvClose
*/
static DWORD PCM_drvClose(DWORD dwDevID)
{
return 1;
}
#define NUM_PCM_FORMATS (sizeof(PCM_Formats) / sizeof(PCM_Formats[0]))
#define NUM_OF(a,b) (((a)+(b)-1)/(b))
/* flags for fdwDriver */
#define PCM_RESAMPLE 1
/* data used while converting */
typedef struct tagAcmPcmData {
/* conversion routine, depending if rate conversion is required */
union {
void (*cvtKeepRate)(const unsigned char*, int, unsigned char*);
void (*cvtChangeRate)(struct tagAcmPcmData*, const unsigned char*,
LPDWORD, unsigned char*, LPDWORD);
} cvt;
/* the following fields are used only with rate conversion) */
DWORD srcPos; /* position in source stream */
double dstPos; /* position in destination stream */
double dstIncr; /* value to increment dst stream when src stream
is incremented by 1 */
/* last source stream value read */
union {
unsigned char b; /* 8 bit value */
short s; /* 16 bit value */
} last[2]; /* two channels max (stereo) */
} AcmPcmData;
/* table to list all supported formats... those are the basic ones. this
* also helps given a unique index to each of the supported formats
*/
static struct {
int nChannels;
int nBits;
int rate;
} 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},
{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},
};
#define NUM_PCM_FORMATS (sizeof(PCM_Formats) / sizeof(PCM_Formats[0]))
/***********************************************************************
* PCM_GetFormatIndex
*/
static DWORD PCM_GetFormatIndex(LPWAVEFORMATEX wfx)
{
int i;
......@@ -65,6 +98,593 @@ static DWORD PCM_GetFormatIndex(LPWAVEFORMATEX wfx)
return 0xFFFFFFFF;
}
/* PCM Conversions:
*
* parameters:
* + 8 bit unsigned vs 16 bit signed
* + mono vs stereo (1 or 2 channels)
* + sampling rate (8.0, 11.025, 22.05, 44.1 kHz are defined, but algo shall work
* in all cases)
*
* mono => stereo: copy the same sample on Left & Right channels
* stereo =) mono: use the average value of samples from Left & Right channels
* resampling; we lookup for each destination sample the two source adjacent samples
* were src <= dst < src+1 (dst is increased by a fractional value which is
* equivalent to the increment by one on src); then we use a linear
* interpolation between src and src+1
*/
/***********************************************************************
* C816
*
* Converts a 8 bit sample to a 16 bit one
*/
static inline short C816(unsigned char b)
{
return (short)(b ^ 0x80) * 256;
}
/***********************************************************************
* C168
*
* Converts a 16 bit sample to a 8 bit one (data loss !!)
*/
static inline unsigned char C168(short s)
{
return HIBYTE(s) ^ (unsigned char)0x80;
}
/***********************************************************************
* 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);
}
/***********************************************************************
* M16
*
* Convert the (l,r) 16 bit stereo sample into a 16 bit mono
* (takes the mid-point of the two values)
*/
static inline short M16(short l, short r)
{
return (l + r) / 2;
}
/***********************************************************************
* M8
*
* Convert the (l,r) 8 bit stereo sample into a 8 bit mono
* (takes the mid-point of the two values)
*/
static inline unsigned char M8(unsigned char a, unsigned char b)
{
return (unsigned char)((a + b) / 2);
}
/* the conversion routines without rate conversion are labelled cvt<X><Y><N><M>K
* where :
* <X> is the (M)ono/(S)tereo configuration of input channel
* <Y> is the (M)ono/(S)tereo configuration of output channel
* <N> is the number of bits of input channel (8 or 16)
* <M> is the number of bits of output channel (8 or 16)
*
* in the parameters, ns is always the number of samples, so the size of input
* buffer (resp output buffer) is ns * (<X> == 'Mono' ? 1:2) * (<N> == 8 ? 1:2)
*/
static void cvtMM88K(const unsigned char* src, int ns, unsigned char* dst)
{
memcpy(dst, src, ns);
}
static void cvtSS88K(const unsigned char* src, int ns, unsigned char* dst)
{
memcpy(dst, src, ns * 2);
}
static void cvtMM1616K(const unsigned char* src, int ns, unsigned char* dst)
{
memcpy(dst, src, ns * 2);
}
static void cvtSS1616K(const unsigned char* src, int ns, unsigned char* dst)
{
memcpy(dst, src, ns * 4);
}
static void cvtMS88K(const unsigned char* src, int ns, unsigned char* dst)
{
while (ns--) {
*dst++ = *src;
*dst++ = *src++;
}
}
static void cvtMS816K(const unsigned char* src, int ns, unsigned char* dst)
{
short v;
while (ns--) {
v = C816(*src++);
W16(dst, v); dst += 2;
W16(dst, v); dst += 2;
}
}
static void cvtMS168K(const unsigned char* src, int ns, unsigned char* dst)
{
unsigned char v;
while (ns--) {
v = C168(R16(src)); src += 2;
*dst++ = v;
*dst++ = v;
}
}
static void cvtMS1616K(const unsigned char* src, int ns, unsigned char* dst)
{
short v;
while (ns--) {
v = R16(src); src += 2;
W16(dst, v); dst += 2;
W16(dst, v); dst += 2;
}
}
static void cvtSM88K(const unsigned char* src, int ns, unsigned char* dst)
{
while (ns--) {
*dst++ = M8(src[0], src[1]);
src += 2;
}
}
static void cvtSM816K(const unsigned char* src, int ns, unsigned char* dst)
{
short v;
while (ns--) {
v = M16(C816(src[0]), C816(src[1]));
src += 2;
W16(dst, v); dst += 2;
}
}
static void cvtSM168K(const unsigned char* src, int ns, unsigned char* dst)
{
while (ns--) {
*dst++ = C168(M16(R16(src), R16(src + 2)));
src += 4;
}
}
static void cvtSM1616K(const unsigned char* src, int ns, unsigned char* dst)
{
while (ns--) {
W16(dst, M16(R16(src),R16(src+2))); dst += 2;
src += 4;
}
}
static void cvtMM816K(const unsigned char* src, int ns, unsigned char* dst)
{
while (ns--) {
W16(dst, C816(*src++)); dst += 2;
}
}
static void cvtSS816K(const unsigned char* src, int ns, unsigned char* dst)
{
while (ns--) {
W16(dst, C816(*src++)); dst += 2;
W16(dst, C816(*src++)); dst += 2;
}
}
static void cvtMM168K(const unsigned char* src, int ns, unsigned char* dst)
{
while (ns--) {
*dst++ = C168(R16(src)); src += 2;
}
}
static void cvtSS168K(const unsigned char* src, int ns, unsigned char* dst)
{
while (ns--) {
*dst++ = C168(R16(src)); src += 2;
*dst++ = C168(R16(src)); src += 2;
}
}
static void (*PCM_ConvertKeepRate[16])(const unsigned char*, int, unsigned char*) = {
cvtSS88K, cvtSM88K, cvtMS88K, cvtMM88K,
cvtSS816K, cvtSM816K, cvtMS816K, cvtMM816K,
cvtSS168K, cvtSM168K, cvtMS168K, cvtMM168K,
cvtSS1616K, cvtSM1616K, cvtMS1616K, cvtMM1616K,
};
/***********************************************************************
* I
*
* Interpolate the value at r (r in ]0, 1]) between the two points v1 and v2
* Linear interpolation is used
*/
static inline double I(double v1, double v2, double r)
{
if (0.0 >= r || r > 1.0) FIXME("r!! %f\n", r);
return (1.0 - r) * v1 + r * v2;
}
static void cvtSS88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].b = *src++;
apd->last[1].b = *src++;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
*dst++ = I(apd->last[0].b, src[0], r);
*dst++ = I(apd->last[1].b, src[1], r);
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
/* the conversion routines with rate conversion are labelled cvt<X><Y><N><M>C
* where :
* <X> is the (M)ono/(S)tereo configuration of input channel
* <Y> is the (M)ono/(S)tereo configuration of output channel
* <N> is the number of bits of input channel (8 or 16)
* <M> is the number of bits of output channel (8 or 16)
*
*/
static void cvtSM88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].b = *src++;
apd->last[1].b = *src++;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
*dst++ = I(M8(apd->last[0].b, apd->last[1].b), M8(src[0], src[1]), r);
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtMS88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].b = *src++;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
dst[0] = dst[1] = I(apd->last[0].b, src[0], r);
dst += 2;
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtMM88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].b = *src++;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
*dst++ = I(apd->last[0].b, src[0], r);
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtSS816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].b = *src++;
apd->last[1].b = *src++;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
W16(dst, I(C816(apd->last[0].b), C816(src[0]), r)); dst += 2;
W16(dst, I(C816(apd->last[1].b), C816(src[1]), r)); dst += 2;
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtSM816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].b = *src++;
apd->last[1].b = *src++;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
W16(dst, I(M16(C816(apd->last[0].b), C816(apd->last[1].b)),
M16(C816(src[0]), C816(src[1])), r));
dst += 2;
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtMS816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
short v;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].b = *src++;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
v = I(C816(apd->last[0].b), C816(src[0]), r);
W16(dst, v); dst += 2;
W16(dst, v); dst += 2;
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtMM816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].b = *src++;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
W16(dst, I(C816(apd->last[0].b), C816(src[0]), r));
dst += 2;
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtSS168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].s = R16(src); src += 2;
apd->last[1].s = R16(src); src += 2;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
*dst++ = C168(I(apd->last[0].s, R16(src) , r));
*dst++ = C168(I(apd->last[1].s, R16(src+2), r));
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtSM168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].s = R16(src); src += 2;
apd->last[1].s = R16(src); src += 2;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
*dst++ = C168(I(M16(apd->last[0].s, apd->last[1].s),
M16(R16(src), R16(src + 2)), r));
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtMS168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].s = R16(src); src += 2;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
dst[0] = dst[1] = C168(I(apd->last[0].s, R16(src), r)); dst += 2;
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtMM168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].s = R16(src); src += 2;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
*dst++ = C168(I(apd->last[0].s, R16(src), r));
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtSS1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].s = R16(src); src += 2;
apd->last[1].s = R16(src); src += 2;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
W16(dst, I(apd->last[0].s, R16(src) , r)); dst += 2;
W16(dst, I(apd->last[1].s, R16(src+2), r)); dst += 2;
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtSM1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].s = R16(src); src += 2;
apd->last[1].s = R16(src); src += 2;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
W16(dst, I(M16(apd->last[0].s, apd->last[1].s),
M16(R16(src), R16(src+2)), r));
dst += 2;
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtMS1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
short v;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].s = R16(src); src += 2;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
v = I(apd->last[0].s, R16(src), r);
W16(dst, v); dst += 2;
W16(dst, v); dst += 2;
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void cvtMM1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst)
{
double r;
while (*nsrc != 0 && *ndst != 0) {
while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
if (*nsrc == 0) return;
apd->last[0].s = R16(src); src += 2;
apd->srcPos++;
(*nsrc)--;
}
/* now do the interpolation */
W16(dst, I(apd->last[0].s, R16(src), r)); dst += 2;
apd->dstPos += apd->dstIncr;
(*ndst)--;
}
}
static void (*PCM_ConvertChangeRate[16])(AcmPcmData* apd,
const unsigned char* src, LPDWORD nsrc,
unsigned char* dst, LPDWORD ndst) = {
cvtSS88C, cvtSM88C, cvtMS88C, cvtMM88C,
cvtSS816C, cvtSM816C, cvtMS816C, cvtMM816C,
cvtSS168C, cvtSM168C, cvtMS168C, cvtMM168C,
cvtSS1616C, cvtSM1616C, cvtMS1616C, cvtMM1616C,
};
/***********************************************************************
* PCM_DriverDetails
*
*/
static LRESULT PCM_DriverDetails(PACMDRIVERDETAILSW add)
{
add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
......@@ -78,7 +698,7 @@ static LRESULT PCM_DriverDetails(PACMDRIVERDETAILSW add)
add->cFilterTags = 0;
add->hicon = (HICON)0;
lstrcpyAtoW(add->szShortName, "WINE-PCM");
lstrcpyAtoW(add->szLongName, "Wine PCM converter");
lstrcpyAtoW(add->szLongName, "Wine PCM converter");
lstrcpyAtoW(add->szCopyright, "Brought to you by the Wine team...");
lstrcpyAtoW(add->szLicensing, "Refer to LICENSE file");
add->szFeatures[0] = 0;
......@@ -86,6 +706,10 @@ static LRESULT PCM_DriverDetails(PACMDRIVERDETAILSW add)
return MMSYSERR_NOERROR;
}
/***********************************************************************
* PCM_FormatTagDetails
*
*/
static LRESULT PCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery)
{
switch (dwQuery) {
......@@ -96,7 +720,8 @@ static LRESULT PCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery)
if (aftd->dwFormatTag != WAVE_FORMAT_PCM) return ACMERR_NOTPOSSIBLE;
break;
case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
if (aftd->dwFormatTag != WAVE_FORMAT_UNKNOWN && aftd->dwFormatTag != WAVE_FORMAT_UNKNOWN)
if (aftd->dwFormatTag != WAVE_FORMAT_UNKNOWN &&
aftd->dwFormatTag != WAVE_FORMAT_UNKNOWN)
return ACMERR_NOTPOSSIBLE;
break;
default:
......@@ -114,6 +739,10 @@ static LRESULT PCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery)
return MMSYSERR_NOERROR;
}
/***********************************************************************
* PCM_FormatDetails
*
*/
static LRESULT PCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery)
{
switch (dwQuery) {
......@@ -128,10 +757,12 @@ static LRESULT PCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery)
afd->pwfx->nSamplesPerSec = PCM_Formats[afd->dwFormatIndex].rate;
afd->pwfx->wBitsPerSample = PCM_Formats[afd->dwFormatIndex].nBits;
/* native MSACM uses a PCMWAVEFORMAT structure, so cbSize is not accessible
afd->pwfx->cbSize = 0;
*/
afd->pwfx->nBlockAlign = (afd->pwfx->nChannels * afd->pwfx->wBitsPerSample) / 8;
afd->pwfx->nAvgBytesPerSec = afd->pwfx->nSamplesPerSec * afd->pwfx->nBlockAlign;
* afd->pwfx->cbSize = 0;
*/
afd->pwfx->nBlockAlign =
(afd->pwfx->nChannels * afd->pwfx->wBitsPerSample) / 8;
afd->pwfx->nAvgBytesPerSec =
afd->pwfx->nSamplesPerSec * afd->pwfx->nBlockAlign;
break;
default:
WARN("Unsupported query %08lx\n", dwQuery);
......@@ -145,44 +776,112 @@ static LRESULT PCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery)
return MMSYSERR_NOERROR;
}
/***********************************************************************
* PCM_FormatSuggest
*
*/
static LRESULT PCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs)
{
FIXME("(%p);\n", adfs);
return MMSYSERR_NOTSUPPORTED;
}
/***********************************************************************
* PCM_Reset
*
*/
static void PCM_Reset(AcmPcmData* apd, int srcNumBits)
{
apd->srcPos = 0;
apd->dstPos = 0;
/* initialize with neutral value */
if (srcNumBits == 16) {
apd->last[0].s = 0;
apd->last[1].s = 0;
} else {
apd->last[0].b = (BYTE)0x80;
apd->last[1].b = (BYTE)0x80;
}
}
/***********************************************************************
* PCM_StreamOpen
*
*/
static LRESULT PCM_StreamOpen(PACMDRVSTREAMINSTANCE adsi)
{
AcmPcmData* apd;
int idx = 0;
assert(!(adsi->fdwOpen & ACM_STREAMOPENF_ASYNC));
if (PCM_GetFormatIndex(adsi->pwfxSrc) == 0xFFFFFFFF ||
PCM_GetFormatIndex(adsi->pwfxDst) == 0xFFFFFFFF)
return ACMERR_NOTPOSSIBLE;
apd = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmPcmData));
if (apd == 0) return MMSYSERR_NOMEM;
adsi->dwDriver = (DWORD)apd;
adsi->fdwDriver = 0;
if (adsi->pwfxSrc->wBitsPerSample == 16) idx += 8;
if (adsi->pwfxDst->wBitsPerSample == 16) idx += 4;
if (adsi->pwfxSrc->nChannels == 1) idx += 2;
if (adsi->pwfxDst->nChannels == 1) idx += 1;
if (adsi->pwfxSrc->nSamplesPerSec == adsi->pwfxDst->nSamplesPerSec) {
apd->cvt.cvtKeepRate = PCM_ConvertKeepRate[idx];
} else {
adsi->fdwDriver |= PCM_RESAMPLE;
apd->dstIncr = (double)(adsi->pwfxSrc->nSamplesPerSec) /
(double)(adsi->pwfxDst->nSamplesPerSec);
PCM_Reset(apd, adsi->pwfxSrc->wBitsPerSample);
apd->cvt.cvtChangeRate = PCM_ConvertChangeRate[idx];
}
return MMSYSERR_NOERROR;
}
/***********************************************************************
* PCM_StreamClose
*
*/
static LRESULT PCM_StreamClose(PACMDRVSTREAMINSTANCE adsi)
{
HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver);
return MMSYSERR_NOERROR;
}
/***********************************************************************
* PCM_round
*
*/
static inline DWORD PCM_round(DWORD a, DWORD b, DWORD c)
{
assert(a && b && c);
/* to be sure, always return an entire number of c... */
return (a * b + c - 1) / c;
return ((double)a * (double)b + (double)c - 1) / (double)c;
}
/***********************************************************************
* PCM_StreamSize
*
*/
static LRESULT PCM_StreamSize(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMSIZE adss)
{
switch (adss->fdwSize) {
case ACM_STREAMSIZEF_DESTINATION:
/* cbDstLength => cbSrcLength */
adss->cbSrcLength = PCM_round(adss->cbDstLength, adsi->pwfxSrc->nAvgBytesPerSec, adsi->pwfxDst->nAvgBytesPerSec);
adss->cbSrcLength = PCM_round(adss->cbDstLength,
adsi->pwfxSrc->nAvgBytesPerSec,
adsi->pwfxDst->nAvgBytesPerSec);
break;
case ACM_STREAMSIZEF_SOURCE:
/* cbSrcLength => cbDstLength */
adss->cbDstLength = PCM_round(adss->cbSrcLength, adsi->pwfxDst->nAvgBytesPerSec, adsi->pwfxSrc->nAvgBytesPerSec);
adss->cbDstLength = PCM_round(adss->cbSrcLength,
adsi->pwfxDst->nAvgBytesPerSec,
adsi->pwfxSrc->nAvgBytesPerSec);
break;
default:
WARN("Unsupported query %08lx\n", adss->fdwSize);
......@@ -191,193 +890,50 @@ static LRESULT PCM_StreamSize(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMSIZE adss
return MMSYSERR_NOERROR;
}
/*
parameters :
8 bit unsigned vs 16 bit signed (-32 / +32k ???)
mono vs stereo
sampling rate (8.0, 11.025, 22.05, 44.1 kHz)
*/
static void cvtMS88K(const unsigned char* src, int ns, unsigned char* dst)
/***********************************************************************
* PCM_StreamConvert
*
*/
static LRESULT PCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER adsh)
{
while (ns--) {
*dst++ = *src;
*dst++ = *src++;
}
}
AcmPcmData* apd = (AcmPcmData*)adsi->dwDriver;
DWORD nsrc = NUM_OF(adsh->cbSrcLength, adsi->pwfxSrc->nBlockAlign);
DWORD ndst = NUM_OF(adsh->cbDstLength, adsi->pwfxDst->nBlockAlign);
static void cvtMS816K(const unsigned char* src, int ns, short* dst)
{
int v;
while (ns--) {
v = ((short)(*src++) ^ 0x80) * 256;
*dst++ = LOBYTE(v);
*dst++ = HIBYTE(v);
*dst++ = LOBYTE(v);
*dst++ = HIBYTE(v);
if (adsh->fdwConvert &
~(ACM_STREAMCONVERTF_BLOCKALIGN|
ACM_STREAMCONVERTF_END|
ACM_STREAMCONVERTF_START)) {
FIXME("Unsupported fdwConvert (%08lx), ignoring it\n", adsh->fdwConvert);
}
}
static void cvtMS168K(const short* src, int ns, unsigned char* dst)
{
unsigned char v;
while (ns--) {
v = HIBYTE(*src++) ^ 0x80;
*dst++ = v;
*dst++ = v;
/* ACM_STREAMCONVERTF_BLOCKALIGN
* currently all conversions are block aligned, so do nothing for this flag
* ACM_STREAMCONVERTF_END
* no pending data, so do nothing for this flag
*/
if ((adsh->fdwConvert & ACM_STREAMCONVERTF_START) &&
(adsi->fdwDriver & PCM_RESAMPLE)) {
PCM_Reset(apd, adsi->pwfxSrc->wBitsPerSample);
}
}
static void cvtMS1616K(const short* src, int ns, short* dst)
{
while (ns--) {
*dst++ = *src;
*dst++ = *src++;
}
}
static void cvtSM88K(const unsigned char* src, int ns, unsigned char* dst)
{
while (ns--) {
*dst++ = (src[0] + src[1]) / 2;
src += 2;
}
}
static void cvtSM816K(const unsigned char* src, int ns, short* dst)
{
int v;
while (ns--) {
v = (((short)(src[0]) ^ 0x80) * 256 + ((short)(src[1]) ^ 0x80) * 256) / 2;
src += 2;
*dst++ = LOBYTE(v);
*dst++ = HIBYTE(v);
}
}
static void cvtSM168K(const short* src, int ns, unsigned char* dst)
{
unsigned char v;
while (ns--) {
v = ((HIBYTE(src[0]) ^ 0x80) + (HIBYTE(src[1]) ^ 0x80)) / 2;
src += 2;
*dst++ = v;
}
}
static void cvtSM1616K(const short* src, int ns, short* dst)
{
while (ns--) {
*dst++ = (src[0] + src[1]) / 2;
src += 2;
}
}
static void cvtMM816K(const unsigned char* src, int ns, short* dst)
{
int v;
while (ns--) {
v = ((short)(*src++) ^ 0x80) * 256;
*dst++ = LOBYTE(v);
*dst++ = HIBYTE(v);
}
}
/* do the job */
if (adsi->fdwDriver & PCM_RESAMPLE) {
DWORD nsrc2 = nsrc;
DWORD ndst2 = ndst;
static void cvtSS816K(const unsigned char* src, int ns, short* dst)
{
int v;
while (ns--) {
v = ((short)(*src++) ^ 0x80) * 256;
*dst++ = LOBYTE(v);
*dst++ = HIBYTE(v);
v = ((short)(*src++) ^ 0x80) * 256;
*dst++ = LOBYTE(v);
*dst++ = HIBYTE(v);
}
}
apd->cvt.cvtChangeRate(apd, adsh->pbSrc, &nsrc2, adsh->pbDst, &ndst2);
nsrc -= nsrc2;
ndst -= ndst2;
} else {
if (nsrc < ndst) ndst = nsrc; else nsrc = ndst;
static void cvtMM168K(const short* src, int ns, unsigned char* dst)
{
while (ns--) {
*dst++ = HIBYTE(*src++) ^ 0x80;
/* nsrc is now equal to ndst */
apd->cvt.cvtKeepRate(adsh->pbSrc, nsrc, adsh->pbDst);
}
}
static void cvtSS168K(const short* src, int ns, unsigned char* dst)
{
while (ns--) {
*dst++ = HIBYTE(*src++) ^ 0x80;
*dst++ = HIBYTE(*src++) ^ 0x80;
}
}
adsh->cbSrcLengthUsed = nsrc * adsi->pwfxSrc->nBlockAlign;
adsh->cbDstLengthUsed = ndst * adsi->pwfxDst->nBlockAlign;
static LRESULT PCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER adsh)
{
/* do the job */
if (adsi->pwfxSrc->nSamplesPerSec == adsi->pwfxDst->nSamplesPerSec) {
/* easy case */
if (adsi->pwfxSrc->wBitsPerSample == adsi->pwfxDst->wBitsPerSample &&
adsi->pwfxSrc->nChannels == adsi->pwfxDst->nChannels) {
memcpy(adsh->pbDst, adsh->pbSrc, adsh->cbSrcLength);
} else if (adsi->pwfxSrc->wBitsPerSample == 8 &&
adsi->pwfxDst->wBitsPerSample == 8) {
if (adsi->pwfxSrc->nChannels == 1 &&
adsi->pwfxDst->nChannels == 2)
cvtMS88K(adsh->pbSrc, adsh->cbSrcLength, adsh->pbDst);
else if (adsi->pwfxSrc->nChannels == 2 &&
adsi->pwfxDst->nChannels == 1)
cvtSM88K(adsh->pbSrc, adsh->cbSrcLength / 2, adsh->pbDst);
} else if (adsi->pwfxSrc->wBitsPerSample == 8 &&
adsi->pwfxDst->wBitsPerSample == 16) {
if (adsi->pwfxSrc->nChannels == 1 &&
adsi->pwfxDst->nChannels == 1)
cvtMM816K(adsh->pbSrc, adsh->cbSrcLength, (short*)adsh->pbDst);
else if (adsi->pwfxSrc->nChannels == 1 &&
adsi->pwfxDst->nChannels == 2)
cvtMS816K(adsh->pbSrc, adsh->cbSrcLength, (short*)adsh->pbDst);
else if (adsi->pwfxSrc->nChannels == 2 &&
adsi->pwfxDst->nChannels == 1)
cvtSM816K(adsh->pbSrc, adsh->cbSrcLength / 2, (short*)adsh->pbDst);
else if (adsi->pwfxSrc->nChannels == 2 &&
adsi->pwfxDst->nChannels == 2)
cvtSS816K(adsh->pbSrc, adsh->cbSrcLength / 2, (short*)adsh->pbDst);
} else if (adsi->pwfxSrc->wBitsPerSample == 16 &&
adsi->pwfxDst->wBitsPerSample == 8) {
if (adsi->pwfxSrc->nChannels == 1 &&
adsi->pwfxDst->nChannels == 1)
cvtMM168K((short*)adsh->pbSrc, adsh->cbSrcLength / 2, adsh->pbDst);
else if (adsi->pwfxSrc->nChannels == 1 &&
adsi->pwfxDst->nChannels == 2)
cvtMS168K((short*)adsh->pbSrc, adsh->cbSrcLength / 2, adsh->pbDst);
else if (adsi->pwfxSrc->nChannels == 2 &&
adsi->pwfxDst->nChannels == 1)
cvtSM168K((short*)adsh->pbSrc, adsh->cbSrcLength / 4, adsh->pbDst);
else if (adsi->pwfxSrc->nChannels == 2 &&
adsi->pwfxDst->nChannels == 2)
cvtSS168K((short*)adsh->pbSrc, adsh->cbSrcLength / 4, adsh->pbDst);
} else if (adsi->pwfxSrc->wBitsPerSample == 16 &&
adsi->pwfxDst->wBitsPerSample == 16) {
if (adsi->pwfxSrc->nChannels == 1 &&
adsi->pwfxDst->nChannels == 2)
cvtMS1616K((short*)adsh->pbSrc, adsh->cbSrcLength / 2, (short*)adsh->pbDst);
else if (adsi->pwfxSrc->nChannels == 2 &&
adsi->pwfxDst->nChannels == 1)
cvtSM1616K((short*)adsh->pbSrc, adsh->cbSrcLength / 4, (short*)adsh->pbDst);
} else FIXME("NIY\n");
/* FIXME: rounding shall be taken care off... */
adsh->cbSrcLengthUsed = adsh->cbSrcLength;
adsh->cbDstLengthUsed = (adsh->cbSrcLength * adsi->pwfxDst->nBlockAlign) / adsi->pwfxSrc->nBlockAlign;
} else {
FIXME("NIY\n");
return MMSYSERR_NOTSUPPORTED;
}
return MMSYSERR_NOERROR;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment