admin管理员组文章数量:1532198
2024年6月27日发(作者:)
糟}{■ ≥蔫曩 III
维普资讯
j
编程语言
ROGRAM AN(、【jAGE
用 Vc 开谖 WINAMP的音效插件 一。, 。: 。。 。
_
一%
√
_f' {廖艳轧荣华
.
。
摘要本文阐述了WINAMP播放器软件的DSP插件制作技术,该插件是对WINAMP播
∥ 谚
黪 ~
放器功能的扩展,它可以将歌曲中的原唱歌声消除,只留下伴唱。
关键词
一
WINAMP,插件,VC++,MFC,DLL,DSP,双二次滤波算法
_
一
、
誊一
日IJ昌
WINAMP是目前应用最广泛的音乐播放器软件之一,它
5.可视化插件(Visualization plug—ins):它用来显示一
些基于音乐的动态效果,增加软件的趣味性。使得音乐不但能
让耳朵享受到,同时让眼睛也来享受一把。
深受广大音乐爱好者的青睐。不仅是因为它强大的功能和漂亮
的界面,最重要的还是该软件的设计结构,它拥有开放的非常
WINAMP主程序根据输入插件注册的文件类型或者输入设
备类型来调用相应的输入插件实现信号从媒体的输入,然后输
入信号流被送到DSP(数字音频信号处理)插件进行进一步的
处理,最后经过处理的信号送到输出插件,输出插件或者将音
频信号从音箱还原,或者将音频信号以某种编码方式写入文
件。这完全依赖输出插件的具体实现而定。
本文详细介绍怎样来实现DSP插件(基于WINAMP2),
灵活的插件接口。全世界的许多软件开发人员,同样也是音乐
爱好者,他们为WINAMP编写了大量的非常实用的功能丰富
的插件。使得该软件常能保持新鲜的活力。对当今网络流行的
各种音乐文件格式提供很好的支持。
事实上,WINAMP几乎是完全依赖于插件的支持而正常
工作,WINAMP主体程序仅仅提供了一个载体,它将各种插
件有机地结合起来,协调工作。
并实现了一个消歌声插件。其它类型的插件可以通过访问
WINAMP官方网站(http://www.winamp.corn)来了解,其他
网站也可以找到一些相关的文章。
二、体系结构
WINAMP的插件是一些用来扩展WINAMP功能的文件,
它以WIN32动态连接库的方式存在于WINAMP安装目录下和
三、相关技术
1.获取WINAMP2的SDK。
plugins目录下,按照一定的格式命名,并导出指定的函数。
WINAMP2支持主要五种不同类型的插件:
1.输入插件(Input plug—ins):它用来扩展WINAMP
的输入功能。通过它,WINAMP可以处理那些原本不能识别
的文件格式。
2.输出插件(Output plug—ins):它用来扩展WINAMP
的输出功能,通过它,WINAMP可以将音频数据以一种不同
在编写插件前,我们首先要从WINAMP网站下载插件
SDK(Software Development Kit)。将下载文件解压缩之后得
到相关的头文件,另外还有一些示例代码。仔细阅读这些示例
对我们的开发将会有很大的帮助。DSP插件的相关头文件只有
一
个,就是dsp.h。
头文件里定义了两个结构和几个函数指针。这些结构用来
定义插件的一些属性和功能。
WINAMP的DSP插件DLL将导出一个知名的函数:
winampDSPGetHeader2,这也是唯一个需要导出的函数。这个
的方式输出到音频播放设备、文件、或者其它任何媒介。
3.通用目的插件(General purpose plug—ins):如果你
需要在WINAMP软件工作的后台做一些其它的与音频数据无
关的事,那么这种插件正是你需要的。
4.效果插件(DSP/effect plug—ins):在音频数据送到
输出之前,WINAMP通过调用DPS插件来对音频做处理。
函数将返回一个描述DLL模块的数据结构。每个插件DLL都
可以包含一个或更多的功能相对独立的DSP插件。WINAMP
主程序通过调用一个插件枚举函数来确定DLL内具体有多少
个插件。对于DLL内的每一个DSP插件,都通过另一个结构
}
维普资讯
鼗露■ ,I薏稿:囊
编程语言
l’R0GRAM l ^NGUAGE
来进行描述。
一i
#
黛
麓
薯 嚣萼攀
“i 一
一勰 椒 -毋 一 瓤
_ _ %
另一个重要的结构是winampDSPHeader,这个结构用来描
winampDSPModule结构用来描述一个DSP插件的信息。结
述本DLL模块的信息,它的定义如下:
构定义如下:
typedefstruc:twinampDSPModule{
char:;:description;
HWND hwndParent;
HINSTANCE hDIIInstance;
void( Config)(struct winampDSPModule this_mod):
int(¥Init)(structwinampDSPModule¥thisj-nod):
int( :ModifySamples)(struct winampDSPModule
this
_
rood,short int¥samples,int numsamples,int bps,int
nch,intsrate):
void( Quit)(structwinampDSPModule: this_mod):
void: userData;
)winampDSPModule;
其中:
description:一个字符串指针,对插件作简要描述。
hwndParent:父窗口句柄,当WINAMP装载该插件之
后,它将填充这个成员,它其实就是WINAMP的主窗口。在
这里一定要注意,我们在做开发的时候一定不能将这个结构定
义为常量,因为主程序会在运行时修改某些值。
hDllInstance:DLL的实例句柄,同上一个成员,在
WINAMP装载该插件的时候,它瘵填充这个成员。
Config:这是一个函数指针,如果需要的话,你可以指定
一
_
个函数来对插件进行一些必要的参数配置。当在WINAMP
插件管理窗口单击配置按钮的时候,该函数被调用。通常在这
一 :
个函数里应该显示一个参数配置对话框。如果你的插件不需要
配置,那么该成员可以指定为NULL,这时,WINAMP插件管
理窗口的配置按钮将显示为不可用状态。
Init:这是~个函数指针。如果需要的话,你可以指定一
|
个函数来对插件进行工作前的初始化工作。如果你指定了这个
函数,那么初始化成功后应该返回0。如果你的插件在工作的
_
时候需要显示一个窗口界面,那么这个函数将是理想之处。如
果你的插件不需要进行工作前的初始化,那么请将这个成员设
为NULL。
ModifySamples:一个函数指针,它指定一个DSP处理函
数,这个函数是必需的,不能为空。这是DSP插件的核心函
数,WINAMP主体程序在工作时将会反复调用这个函数来处
理音频数据。这个函数的参数含义以及如何实现,将在本文的
实现部分再作详细介绍。
Quit:一个函数指针,当插件被WINAMP卸载的时候,它
将被调用。在这个函数里,我们做一些最后的清理操作,如关
闭工作窗口,释放申请的内存,保存工作参数等。
userData:可选参数。在这里可以保存一些你认为需要的
数据。
、
I_;=一 与满
ytpedef struct{
intversion;//DSP DRVER
char description;//description of library
winampDSPModule:j:( getModule)(int):
/module retrieval function
)winampDSPHeader;
其中:
version:这是一个固定值,应填充常数DSP_HDRVER,
它表示DSP插件的版本。常数DSP_HDRVER在头文件里被定
义为0x20,表示本插件是用于WINAMP的。
description:一个字符串,对DLL做一个简要描述,它将
显示在插件管理窗口里。
getModule:一个函数指针,WINAMP通过调用这个函数来
枚举该DLL模块内的所有DSP插件。这个函数是必需的。我
们将在本文的实现部分对它进行详细描述。
DLL的导出函数的类型说明如下:
,/exported symbols
typedef winampDSPHeader ( winampDSPGetHeader-
Type)():
2.消歌声原理
通常,在立体声歌曲里,为了体现伴奏音乐的空间感,它
在左右两个声道的分布是有比较大的区别的。但是人声却正好
相反,为了使听众感觉歌声的定位准确,它在左右两个声道的
分布基本是一致的,包括信号的幅度和相位。当然,在实际的
歌曲中不可能完全做到,特别是当歌曲的录音质量比较差的时
候。但是质量好的歌曲,其人声在左右声道的差别还是很小
的。我们正是利用这一点,从歌曲里区分出人声和伴奏的。
但是仅仅利用这一点还是不够的,因为在歌曲里,往往还
有相当多的低频成份,而人耳有对低频声音的不敏感以及低频
声音本身的定位困难的特点。所以往往在歌曲里,伴奏的低频
音乐信号在左右声道中也是非常相似的。如果我们在进行消歌
声的同时也大大消除了伴奏中的低频音乐,那么伴奏听起来就
没有力度,会有一种虚无飘渺的感觉。通常的做法是,我们先
将人声频段从背景音乐中分离出来,因为相对来说,人声的频
段比起整个音乐的频段来说要窄得多。这样就可以尽可能地保
留原来音乐的风格不变。
人耳能听到的声音频率约为20—20KHz,对于现在流行的
高品质音乐,信号一般都是包含声音的整个频段的,如CD音
乐。但超过15KHz的声音,大多数人都很难听到了。
人声的频率范围大约在300~3400Hz之间,男声和女声
略有差别。利用这个特点,我们先通过带通滤波器将人声频段
从歌曲中分离出来,然后将左右声道的人声相减,因为人声在
一lll珏
r ,_
蛙一
羹
… .=
。
一一
w
维普资讯
謦 ≥
瘩骘 萼謦蠢
蠢
辨黔譬纛勰
。 t一 舞_ 垮薯 。 二 _
∥ 一
编程语言
PROGRAM I_AN( {AGE
f:滤波器的截止频率。r:信号的采样频率。Q:滤波
器的品质因数。
左右声道的分量一致,这样该频段内的人声就相互抵消了。分
离出人声后的左右声道音乐由于缺少关键的中频段,所以如果
直接播放出来的话会显得很空洞。这样,我们将消除了人声之
后的中频段再分别加到左右两个声道上去,这样处理之后。人
四、实现
声基本上消除了,同时也最大地保留了伴奏音乐的本来面目。
J
|.P=—= ..;
入
▲
▲一.
混 r
、
输出
●
自道 左声il
二一垒 ^,t减法 卜t —’l
V
r一
、
r
f道 右声娃
.
_ =・ 一.1
;
消歌声处理流程图
3.双二次滤波算法
双二次滤波算法是一种简单而快速的数字信号滤波算法。
通过计算出的不同的滤波器参数。它可以完成低通滤波,高通
滤波,带通,带阻等多种运算。双二次滤波器算法的基本公式
如下:
y =aoxn+alx ~I+a2xn—blYn—l—b2yn 2
其中:
Y :第n次输出。x :第n次输入。
x n--:第n一1次输入…X z:第n一2次输入。
Y 一第n一1次输出…Y::第n一2次输出。
ao,aI,a2,bl,b2:滤波器系数。
●高通滤波器系数按如下方式计算:
2nf sirrn 1
, ,
%=
号(1+c0s(t,),al:一s(1+c0s(t,),龟=%
bl=一2scosoJ,b2=(1一a)
●低通滤波器系数按如下方式计算:
2nf sinm 1
了’ 酉
Oo:
号(1一co鲫), =s(1一c0s(t,), =Oo
bl=一2scosoJ,b2:(1一a)s
其中:
为了方便起见,我们使用了VC6开发环境。并且使用了
MFC库来编写。
1.生成程序框架
用向导生成一个基于MFC的DLL工程的基本框架。我们
在这个框架的基础上进行进一步的开发。我们需要修改一下工
程的设置,以便进行调试并看到WINAMP加载我们的控件之
后的效果。
打开工程属性对话。在LINK页,将工程的输出文件路径
改到WINAMP2的安装目录下的plugins目录下,并修改输出
文件的文件名为dsp_if.lter,dll。比如,你的WINAMP安装在C:、
Program Files、winamp、下,则设置输出文件为:C:、Program
Files、winamp、plugins\dsp filter,dll。切换到Debug标签下,为
调试器设置可执行文件的路径,浏览到WINAMP的主执行文
件winamp,exe,选中即可。这样设置之后,当我们编写好代
码之后就可以调试了,也可以直接运行查看结果了。
2,双二次滤波算法封装类
我们首先对双二次滤波算法进行C++类封装。从菜单选
择插入新类,取名为Cfilter。
向Cfilter类添加几个函数和变量,其中粗体部分为薪加
的:
class CFilter
f
public:
CFilter《):
virtual~CFilter《):
//设置滤波器参数
voidSetFilterParament(intfrequency,floatQ=1.Of):
//滤波函数
vitrual void Filter(short pData,int num,int nch,int srate):
protected:
//计算滤波系数
vitrual void CalcFilterParament《)=0:
int_sr;//采样频率
int.
_
f;//截止频率
float ://品质因数
float
_
aO,
_
al,-a2://滤波器参数
float_j)1,一b2:
private:
//历史值
int
_
xnl
_
xn2
__
1
_
ynl
_
yn2
int xnl-2.._xn2 。_ynl-2,_yn2.2:
)
constfloatPI=3.1415926f;// 值
鼍
¨1 。
维普资讯
j
襄藕嘲— ■囊薯譬
¨1
¨
i?
=
+
,一
,
编程语言
PR0GRAM t ANGU^G
。 一
//截断函数
//template<class T>
inline int B0UND(intx,int min,int max)
f
return(x<min)7min:((×>max)?max:x):
}
这个类是封装的基本双二次滤波算法,我们将计算滤波系
数的成员函数定义为纯虚函数。因为它不知道如何计算滤波算
法系数。这样,我们就不能对这个类进行实例化,我们需要从
这个类派生出具体的滤波器类,因为只有一个具体的滤波器才
知道怎样来计算它的系数。
我们需要这个类里的几个关键函数。SetFilterParament函
数用来设置滤波器参数,在这个函数里,我们应该将参数保留
下来,并调用滤波系数计算函数去计算实际工作时的滤波系
数。Filter函数是滤波算法的核心函数r因为不同的滤波器仅
仅表现为滤波器系数的差别,而算法都是一样的,所以应该在
基类里实现它。另外我们还需要在类的构造函数里添加成员变
量的初始化代码。
部分源代码如下:
////////////////////////////////////
//COnstructiOn/Destruction
///////////////////////////////////
CFilter::CFilter()
(
//初始化参数
_
xnl_1=0,_xn2j=0,_ynlj=0,_yn2_1=O:
_
xnl-2=0._xn2_2=0,_ynl-2=0,_yn2_2=o=
_
a0=1.Of._al=1.Of,
_
a2=1.Of;
_j)1=1.0f._j)2=1.0f:
_
sr=44100;
j=1000;
=1.Of;
}
¨_一
CFilter::~CFilter()
(
}
//设置滤波参数
void C Filter::SetFilterParament(int frequency,float Q)
(
j=frequency;
=Q:
CalcFilterParament():
}
//滤波函数
voidCFilter::Filter(short pData,intnum,intnch.intsrate)
(
//如果采样率改变,应重新计算参数
_f(srate!=_sr)
(
与
r srate;
CalcFilterParament():
}
iflnch==2)//双声道
(
short I pData;
short r=pData-i-1:
inti=num:
while(i一一>0)
( -
//计算输出
intlyn=(int)(_a0 :( ”-i-_a1%xn1j-i-_a2 _xn2_l
一
1¥
_
ynl_1一_!)2 _yn2.1-):
intryn:(int)(_a0 ( r)-i-.al¥_xnl -i-_a2 :_xn2_2
一
-b1 _ynl 一_!)2’ _yn2-2):
//更新历史数据
xn2
_
l=
__
xnlj:_xnl_1=; I:
—
yn2_1=_ynlj:_ynl_1=lyn;
_
xn2-2=_xnl_2:_xnl-2= r:
_
yn2-2:_ynl_2:_ynl_2=ryn;
//截断至16位
lyn=BOUND(1yn,-32768,32767):
ryn=BOUND(ryn,一32768,32767):
I:lyn;
r=ryn:
I-i-=2:
r-i-=2:
}
)
else_f(nch==1)//单声道
(
short m=pData;
int;=num:
while(i一一>0)
(
intmyn=(int)(_a0 ( m)+-a1 _xnlj-i-_a2 n2J
一
_j)1¥_ynlj一_b2 _yn2_1):
if(myn>32767)myn:32767;
elseif(myn<-32768)myn=~32768;
_
xn2_1 _xnl_1:_xnl_1= m:
_
yn2
_
l=
_
ynlJ:_ynlj=myn;
m=myn;
m++:
}
}
}
下面,我们要从这个滤波器的基类派生出有实际意义的几
个滤波器。
(1)高通滤波器类的说明:
#include Filter.h
//高通滤波器
classCHighPass ter:publicCFilter
海
嚣孽叠 ;薯
维普资讯
_¨
{
public:
virtual void CalcFilterParament():
}:
这个类很简单,我们只需要实现计算滤波系数的函数即
可。
//计算高通滤波器参数
void CHighPass ter::CalcFilterParament{)
{
floatomega=(2.Of PI:l=j)/一sr:
floatsin
_
omega=sinf(omega):
floatcos
_
omega=cosf(omega):
floatalpha=sin
_
omega/(2.Of% ):
floatscalar=1.Of/(1.0f+alpha):
_
a0=0.5f:l=(1.0f+cos__omega): scalar;
_
al=一(1.0f+cos_omega) scalar;
_
a2=j0:
_b1=一2.Of===cos omega scalar;
一b2=(1.Of—alpha):=:scalar;
}
(2)低通滤波器类的说明:
#include Filter.h
//低通滤波器
class CLowPassFilter:public CFilter
{
public:
vitrual void CalcFilterParament():
}:
同高通滤波器一样,我们只需要实现计算滤波系数的函数
即可:
//计算低通滤波器参数
void CLowPassFilter::CalcFilterParament{)
{
floatomega=(2.Of¥PI j)/_sr:
floatsin
_
omega=sinf(omega):
floatcos
_
omega=cosf(omega):
floatalpha=sin
_
omega/l2.Of¥ ):
floatscalar=1.Of/(1.0f+alpha): ・
aO=0.5f}(1.Of—cos_omega):=:scalar;
_
al=(1.0f—cos_omega) scalar;
a2=j0
_b1=一2.Of cos_omega scalar;
-j)2=《1.Of—alpha) scalar; ’
}
带通滤波器有点特殊,它是一个高通滤波器和一个低通滤
波器的串联,我们让它同时继承高通滤波器和低通滤波器两个
类,类说明如下:
#include HighPassFilter.h
#include LowPassFilter.h
//带通滤波器
class CBandPassFiIter:
曩  ̄.411■穗
编程语言
PR0(jRAM l ANt;ljA(j
public CHighPassFlIter,
public CLowPassFilter
{
public:
//滤波函数
void Filter(Short pData,intnum,intnch.intsrate):
//设置滤波参数
void SetFilterParament(int hf,int If,float hQ=1.Of,float
IQ=1.0f):
}:
因为是两上滤波器的串联应用,所以这里我们重写了基类
酌Filter函数,部分源代码如下:
//滤波函数
VoidCBandPassFiIter::Filter(short pData,intnum,intnch
int srate)
{
//一个高通和一个低能滤波器串联操作
CHighPassFiIter::Filter(pData,num,nch,srate)
CLowPassFilter::Filter(pData,num,nch,srate)
}
void CBandPassFiIter::SetFilterParament(int hf,int If,float
hQ,floatIQ)
{
CHighPassFiIter::SetFilterParament{hf,hQ):
CLowPassFilter::SetFilterParament(If.IQ):
}
3.主界面
在窗13上,设置了一个开关,还有几个调节频段和平衡的
控件。见下图。图下面则显示一些统计数据。篇幅所限这里不
再列举那些跟界面控制相关的代码了。
插件运行时的主界面
4.实现DSP插件规定的接口
DSP插件需要实现的接13其实在前面已经说得很清楚了。
我们自顶向下来一步步实现它。首先,我们来实现插件必需
导出的函数:winampDSPGetHeader2。这个函数很简单,它要
一 ..
_
。
甄胃囊菇。0麓囊蠢鬃 。
维普资讯
—
i
编程语言
l,R0 ;RAM l。AN(jUAG
鼍 謦1
做的就是返回代表本插件的一个数据结构。我们将插件接口有
关的代码全部放在一个单独的源文件内,Module.cpp。文件的
开始应该包含dsp.h头文件,我们假设你已经将dsp.h头文件
拷贝到了当前工程的源代码目录下。
GetModule是另一个跟整个插件DLL有关的重要函数,它
的作用就是被WINAMP主程序调用来枚举DLL内的所有插件
信息。这个函数有一个参数,它是一个整数索引号。WINAMP
主程序将以从0开始的索引号来反复地调用这个函数。这个函
数的任务也很简单,就是返回第0个DSP插件信息描述结
构、第1个DSP插件信息描述结构,等等。那么如果没有那
么多插件怎么办?该函数应该在当索引值超过插件数时返回
NULL。这时,WINAMP就知道枚举结束了。这个函数的指针
从模块信息数据结构中传递给WINAMP主程序。
Init函数用来对插件进行初始化,它的工作是新建一个工
作对话框(见主界面部分)。相应地,我们要实现Quit函
数,在这里将对话框删除。
DoFflter函数用来实现音频数据的处理,它直接调用对话
框类的相应函数来实现的。具体的处理过程是在对话框类的成
员函数Filter内完成的。这个函数有几个比较重要的参数。这
些参数是直接从WINAMP主程序里传人的。其中:
samples:指向音频信号数据缓冲区(16Bit),它是混合
数据,即多个声道混杂在一起的。例如,对于双声道信号,第
1个数据应该是声道1的第一个数据,第2个数据则是声道2
的第一个数据,第3个数据是声道1的第二个数据,以此类
推。
numsamples:每个声道的数据量,如果是多个声道,那
么,总的数据量应该是它乘以声道数。
bps:信号的比特数,8Bit或者16Bit。
neh:音频信息的声道数, 立体声信号的声道数应该是
2。
srate:信号的采样频率,指每个声道每秒钟的采样数。
当数据处理完成之后,函数返回处理后的数据量,通常就
是传人函数的参数numsamples的值。然而,你也可以返回其
它值,但不应大于numsamples的2倍,也不应小于它的一
半。WINAMP在准备好数据调用你的处理函数的时候。对音
频数据的缓冲区是有一定的余量的,为输人数据量的2倍。于
是你有机会去修改音频数据的总量。你可以试试会产生什么样
的效果。
Config函数原本是用来对控件进行配置的。在这里,我们
不需要进行配置,因为所有的参数都可以在工作界面窗口内实
时地进行调整。然而,我们还是利用了一下这个函数,它做了
一
件跟配置完全不同的另一件事:显示插件的About对话
 ̄2006.5
与‘
框;)
以上几个函数都有一个共同的参数:this_mod。它既是代
表本插件的数据结构,也就是我们从GetModule函数中返回
的。
Iint,Quit,DoFiher,Config,四个函数共同完成一个DSP
插件的完整功能。它们的指针放在表示插件信息的结构modl
中由函数GetModule传递到WINAMP主程序内。
下面是接口实现的关键代码:
//Module.cpp一一一一实现DSPplug—ins接口
#include dsp.h
#include FilterDIg.h
winampDSPModule GetModule《int which):
//插件枚举函数
void Config l struct winampDSPModule: this
_
rood)
//显示关于对话《利用配置函数)
int Ink《structwinampDSPModule: this_rood):
//插件初始化
void Quit《struct winampDSPModule{this_rood):
//插件退出前的清理
int DoFilter《struct winampDSPModule:#this
—
rnod,
short int samples,int numsamples,
intbps,intnch,intsrate)://音频数据处理
//插件DLL的信息描述数据结构
winampDSPHeader hdr=(
DSP
.
HDRVER .
消歌声插件forWlNAMP2 ,
GetModule
):
//DLL的唯一导出函数,它返回DLL的信息描述数据结构
指针
extern C
__
declspec《dllexport)winampDSPHeader*winampDSPGet-
Header2《)
(
return&hdr;
)
//消歌声插件信息描述数据结构
winampDSPModule orod 1=
(
消歌声处理 ,
NUL L. //hwndParent
NULL, //hDIIInstance
Config,
Init,
DoFilter,
Quit,
NULL
)
winampDSPModule=::GetModule《int index)
(
switchlindex)
(
^
~
一
囊
著■ 旗裁
§麓 鼙蓍 j
鼍 一 一艚
蜜胃l簟 誓■■囊,
维普资讯
一
辑; 羹一。 毫
0
caseO:return&modl:
default:return N U LL;
}
//全局工作对话框指针,当插件被初始化时,工作对话框创
//建,插件被卸载时,对话框被删除
CfilterDIg g
_
pDIg=NULL;
//数据缓冲区
short bufl【65536】://带通滤波缓冲器
short buf2【65536 J://低通滤波缓冲器
short buf3【65536l://高通滤波缓冲器
//消人声函数
void CFilterDIg::Filter{short★pData,int num,int nch,int
srate)
{
//更新统计数据
m
—
nch=nch;
m-srate=srate;
m
_
bytes 4-=num:
m
_
calls 4-4-:
//应该在双声道下工作
if{nch==2&&m-j)Enable)
{
//将音频数据分解成高频段,低频段,及包含人声的中频段
memcpy{bufl,pData,num nch sizeof{short)):
memcpy{buf2,pData,num%nch¥sizeof{short)):
memcpy{buf3,pData,num nch sizeof{short)):
m
_
filterB.Filter{bufl,num,nch,srate):
m_f,ilterL.Filter{buf2,num,nch,srate):
m
_
filterH.Filter{buf3,num,nch,srate):
short a=bufl:
short¥b=buf2;。
short¥C=buf3;
short out:pData;
for{inti=O:i<num:++i)
{
//对人声频段做差运算
intisb={int){a【0】★mJs—a【1】-k m』s 4-0.5f):
//合成新的左右声道
intI=isb 4-b【0】4-C【0】:
int r:isb 4-b【1】4-C【1】:
//截断至16位
I:BOUND{I,.-32768,32767):
r=BOUND{r,一32768,32767):
//输出
out【0】=I:
out【1】=r:
・
a 4-=2:
b 4-=2:
C 4-=2:
out 4-=2:
}
}
}
编程语言
PROGRAM l AN(itj^【jE
//显示关于对话框
void Config{struct winampDSPModule: this_mod)
{
::MessageBox{this_mod一>hwndParent, 使用双二
次滤波器的消歌声程序\n
Copyright{C)2005 , 消歌声插件 ,MB_OK l
MBJCONWARNING):
}
//初始化函数{显示调节对话框)
intInit{structwinampDSPM0dule this_mod)
{
AFX_MANAG E
_
STATE{AfxGetStaticModuleState{)):
ifIg_pDIg!=NULL)deletegJoDIg;
gdoDIg=new CFilterDIg I CWnd::FromHandle
{this_mod一>hwndParent)):
gdoDIg一>Create{):
gdoDIg一>ShowWindow(SW_SHOW):
return O:
}
//退出处理
void Quit{struct winampDSPModule%this_mod)
{
AFX
_
MANAGE
_
STATE{AfxGetStaticModuleState{)):
.f{gjoDIg)
gdoDIg一>DestroyWindqw{):
gdoDIg=NULL;
}
//音频处理函数
int DoFilter{structwinampDSPModule this__mod.
short int samples,int numsamples,
int bps,int nch,int srate)
{
ifIbps==16)
{
.f{gJoDIg!=NULL)
gJoDIg一>Filter{samples.numsamples,nch,srate):
}
return numsamples;
}
5.实际运行
完成插件的代码编写之后,编译工程。这样插件DLL应
该已经生成,并且已位于正确的位置了。然后启动WINAMP
播放器,或者直接利用开发环境的运行命令。因为我们已经设
置好了WINAMP播放器为调试主文件。
注意,WINAMP出现在屏幕上以后,WINAMP并不会自
动加载我们编写的插件,需要对WINAMP设置一下,指定运
行插件才行。
选择WINAMP主菜单一>选项一>参数设置...,或者直接
按快捷键CTRL+P,打开WINAMP参数设置对话框。左边的
树上选中插件下的音效处理,特效。这时,右边列表里已经出
现了一条名为“消歌声插件for WINAMP2【dsp_ifher.dll】.'的
维普资讯
囊■ 弩曩■薯簟
编程语言
PROGRAM l ANG JA(;
仿函数配接器的扩展和实现
张斌
摘 要
C++标准库的STL中用到了仿函数(也称为函数对象)的概念,实际上,其中
所有的算法都提供了一个用仿函数作为其参数的重载版本,从而决定其行为。
对于每个普通的一元和二元运算符,标准库都提供了一个仿函数,对于组合而
言,标准库还提供了几个标准的仿函数和配接器,然而’C++标准库并没有
提供足够的组合型配接器,用于支持仿函数之间的组合。本文将对一些常用的
仿函数组合进行扩展,并具体实现出几个自定义仿函数和几个常用的组合型配
接器。
关键词
仿函数(functors),函数对象(function objects),函数配接器(function
adapters),组合型配接器(compose adapters)
在标准c++中,其强大的重载功能几乎无所不能,所谓
仿函数,就是运用c++强大的运算符重载功能,在一个类中
把重载了operator()运算符的函数作为类的成员函数,并通
过该类的一个对象,可以象调用普通函数那样对其调用。从形
实现。然而,C++标准库并没有提供足够的组合型配接器,
用于支持仿函数之间的组合,在SGI STL和Boost中提供了一
些常用的组合,如f(g(x))、f(g(x,Y))、f(g.(x),h
(x))、f(g(x),h(y))等。
本文将讨论几个常用的仿函数和组合型配接器的实现方法
及具体实现:
自定义仿函数:pow_functor,sin functor,cos_functor,自定
义仿函数配接器:f(g(x),h(y))、z(f(g(x),h
(y)))、z(f(g(x)),f(h(x))其中:z,f,g,h,
分别为任意函数,x,Y为任意类型的参数。
式上看,仿函数的定义比起普通的函数定义要显得复杂,但在
功能上却有其独到的特点。第一,因为是通过类的对象来引
用,因此仿函数可以拥有内部状态。第二,因为定义为类的成
员,因此仿函数是有类型的。第三,仿函数在执行速度上比一
般函数指针要快。基于其自身优越的性能和特点,仿函数在
C++标准库的STL中得到了广泛地应用。
一
般而言,几乎所有的函数行为都可以由仿函数的组合而
\
选项,它就是刚刚写的插件。如果没有看到它,那么就要检查
一
插件的开发,完全跟DSP插件类似。另外其它版本的插件开
发与本文介绍的2.x版本的开发基本一致,可以通过访问
下你的工程,同时要确定一下你的WINAMP版本是否为
“2.x”。一定要选择与你的WINAMP版本相应的SDK来编写
http://www.winamp.com/网站,阅读相关的资料来进行开
发。
你的插件。
在插件列表内选中我们的插件后,插件的界面就出来了。
放一首歌,可以慢慢调整一下高频值和低频值,还有平衡,使
得消声效果最好。
参考文献
1.http://www.winamp.com/nsdn/winamp/sdk/,Winamp
Software Development Kit,NullSofl
五、总结
本文介绍了怎样开发WINAMP播放器软件的插件,并详
2.Phil Burk.使用双二次共振滤波器.游戏编程精粹
(3).人民邮电出版社,2003年7月
(收稿日期:2006年3月26日)
细讲解了DSP插件,一个消歌声插件的开发过程。其它类型
毫■囊藿按_巧与
版权声明:本文标题:用VC开发WINAMP的音效插件 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1719499977a781922.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论