⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcmconverter.c

📁 winNT技术操作系统,国外开放的原代码和LIUX一样
💻 C
📖 第 1 页 / 共 3 页
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- */

/*
 *      MSACM32 library
 *
 *      Copyright 2000		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
 *
 *	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 "config.h"

#include <assert.h>
#include <stdarg.h>
#include <string.h>

#include "windef.h"
#include "winbase.h"
#include "mmsystem.h"
#include "mmreg.h"
#include "msacm.h"
#include "wingdi.h"
#include "winnls.h"
#include "winuser.h"

#include "msacmdrv.h"
#include "wineacm.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(msacm);

/***********************************************************************
 *           PCM_drvOpen
 */
static	DWORD	PCM_drvOpen(LPCSTR str, PACMDRVOPENDESCW adod)
{
    TRACE("(%p, %p)\n", str, adod);

    return (adod == NULL) ||
	(adod->fccType == ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC &&
	 adod->fccComp == ACMDRIVERDETAILS_FCCCOMP_UNDEFINED);
}

/***********************************************************************
 *           PCM_drvClose
 */
static	DWORD	PCM_drvClose(DWORD dwDevID)
{
    TRACE("(%ld)\n", 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, 48000}, {2,  8, 48000}, {1, 16, 48000}, {2, 16, 48000},
    {1,  8, 96000}, {2,  8, 96000}, {1, 16, 96000}, {2, 16, 96000}
};

/***********************************************************************
 *           PCM_GetFormatIndex
 */
static DWORD PCM_GetFormatIndex(LPWAVEFORMATEX wfx)
{
    int i;
    TRACE("(%p)\n", wfx);

    for (i = 0; i < NUM_PCM_FORMATS; i++) {
	if (wfx->nChannels == PCM_Formats[i].nChannels &&
	    wfx->nSamplesPerSec == PCM_Formats[i].rate &&
	    wfx->wBitsPerSample == PCM_Formats[i].nBits)
	    return i;
    }
    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+(b << 8))-32768);
}

/***********************************************************************
 *           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)
{
    TRACE("(%p, %d, %p)\n", src, ns, dst);
    memcpy(dst, src, ns);
}

static	void cvtSS88K(const unsigned char* src, int ns, unsigned char* dst)
{
    TRACE("(%p, %d, %p)\n", src, ns, dst);
    memcpy(dst, src, ns * 2);
}

static	void cvtMM1616K(const unsigned char* src, int ns, unsigned char* dst)
{
    TRACE("(%p, %d, %p)\n", src, ns, dst);
    memcpy(dst, src, ns * 2);
}

static	void cvtSS1616K(const unsigned char* src, int ns, unsigned char* dst)
{
    TRACE("(%p, %d, %p)\n", src, ns, dst);
    memcpy(dst, src, ns * 4);
}

static	void cvtMS88K(const unsigned char* src, int ns, unsigned char* dst)
{
    TRACE("(%p, %d, %p)\n", src, ns, dst);

    while (ns--) {
	*dst++ = *src;
	*dst++ = *src++;
    }
}

static	void cvtMS816K(const unsigned char* src, int ns, unsigned char* dst)
{
    short	v;
    TRACE("(%p, %d, %p)\n", src, ns, dst);

    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;
    TRACE("(%p, %d, %p)\n", src, ns, dst);

    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;
    TRACE("(%p, %d, %p)\n", src, ns, dst);

    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)
{
    TRACE("(%p, %d, %p)\n", src, ns, 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;
    TRACE("(%p, %d, %p)\n", src, ns, dst);

    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)
{
    TRACE("(%p, %d, %p)\n", src, ns, 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)
{
    TRACE("(%p, %d, %p)\n", src, ns, 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)
{
    TRACE("(%p, %d, %p)\n", src, ns, dst);

    while (ns--) {
	W16(dst, C816(*src++));		dst += 2;
    }
}

static	void cvtSS816K(const unsigned char* src, int ns, unsigned char* dst)
{
    TRACE("(%p, %d, %p)\n", src, ns, 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)
{
    TRACE("(%p, %d, %p)\n", src, ns, dst);

    while (ns--) {
	*dst++ = C168(R16(src));	src += 2;
    }
}

static	void cvtSS168K(const unsigned char* src, int ns, unsigned char* dst)
{
    TRACE("(%p, %d, %p)\n", src, ns, 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;
    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);

    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 */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -