自己动手用VC开发WINAMP的音效插件.doc_第1页
自己动手用VC开发WINAMP的音效插件.doc_第2页
自己动手用VC开发WINAMP的音效插件.doc_第3页
自己动手用VC开发WINAMP的音效插件.doc_第4页
自己动手用VC开发WINAMP的音效插件.doc_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

自己动手,用VC开发WINAMP的音效插件本人应朋友之需要,动手写了一个基于WINAMP2的音效插件:消歌声处理。 相关的开发文档均来自于WINAMP的官方网站,如果你对些感兴趣,可访:/来了解关于插件开发的详细资料, 下面我简单地介绍一下DSP(音效)插件的编写。并给出部分源代码,完整的代码请从这里下载:()。WINAMP2的插件是一个WIN32的动态链接库,它位于WINAMP的安装目录下的plugins目录里,每个插件都符合一定的命名规则,并有一个指定的导出函数。WINAMP主程序枚举该目录下的这些DLL,来确定你安装了哪些插件。WINAMP的插件有许多种。你打开WINAMP的插件配置就看到了。音效插件是其中的一种。在WINAMP中简称为DSP。该类插件WINAMP规定其命名方式为dsp_*.dll,其中的“*”为你自已定义的一个任意名字。每个DSP插件都导出一个名为“winampDSPGetHeader2”的函数。它返回一个模块头信息结构的地址。WINAMP网站给开发者们提供了一个DSP模块的头文件。对该结构及函数作了定义,该头文件名为dsp.h,内容如下:#ifndef _WINAMP_DSP_H_#define _WINAMP_DSP_H_#if _MSC_VER 1000#pragma once#endif / _MSC_VER 1000/ DSP plugin interface/ notes:/ any window that remains in foreground should optimally pass unused/ keystrokes to the parent (winamps) window, so that the user/ can still control it. As for storing configuration,/ Configuration data should be stored in plugin.ini/ (look at the vis plugin for configuration code)typedef struct winampDSPModule char *description;/ description HWND hwndParent;/ parent window (filled in by calling app) HINSTANCE hDllInstance;/ instance handle to this DLL (filled in by calling app) void (*Config)(struct winampDSPModule *this_mod); / configuration dialog (if needed) int (*Init)(struct winampDSPModule *this_mod); / 0 on success, creates window, etc (if needed) / modify waveform samples: returns number of samples to actually write / (typically numsamples, but no more than twice numsamples, and no less than half numsamples) / numsamples should always be at least 128. should, but Im not sure int (*ModifySamples)(struct winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate); void (*Quit)(struct winampDSPModule *this_mod); / called when unloading void *userData; / user data, optional winampDSPModule;typedef struct int version; / DSP_HDRVER char *description; / description of library winampDSPModule* (*getModule)(int);/ module retrieval function winampDSPHeader;/ exported symbolstypedef winampDSPHeader* (*winampDSPGetHeaderType)();/ header version: 0x20 = 0.20 = winamp 2.0#define DSP_HDRVER 0x20#endif /* _WINAMP_DSP_H_ */该文件中也对一些东西进行了简要的描述。每个DSP DLL中都可以包含N个相互独立的不同插件。每个插件都对应一个数据结构。WINAMP通过一个函数并指定一个从0开始的索引号来取得这些插件的数据。winampDSPModule结构描述了每个插件的数据。其实主要就是几个指向函数的指针。Init函数在模块加载的时候被调用。你可以在这个函数的实现里初始化需要的数据。生成窗口等。Config函数在需要显示一个配置对话的时候被调用。ModifySamples函数是个主要的函数,它会在插件工作的时候被很快速地反复调用。在这里你对来自WINAMP的波形数据进行处理。该函数应该有足够高的效率。Quit函数在模块卸载时被调用,你可以在这里做些清理工作。userData是一个归你使用的指针,你可以让它指向你自己的任何数据,通常一般也用不到。hwndParent是父窗口的句柄,WINAMP主程序将填充这个数据,它就是你工作的主窗口。hDllInstance是模块DLL的实例句柄。WINAMP主程序会填充这个数据。好了,也就这么多。我们所要做的,就是实现这些函数,并定义这些数据结构。WINAMP的网站提供了一个小小的DSP工程来给我们参考,建议你仔细看看。对你会的很大的帮助。值得一提的是,在该例子工程中包含了三个插件,其中之一就是一个简单的消歌声程序。它所做的就是简单的将左右声道相减。我们要做的当然不是这么简单的。我们要消声的同时最大限度地保持音乐的原来效果。我的消哥声基于同样的原理:通常立体声歌曲的人声在左右声道的分量上相同的,而大部分乐器声则以不同的分量分布在左右声道。但是有一点:由于人耳对低频段的声音定位不准,所以低频乐声通常也是左右声道均匀的。直接左右声道虽然能很好地消除人声但对原音乐的低频分量也会有严重的衰减。消声后的音乐就听起来很空,没有力度。于是消声采用以下方法:分别从左右声首提取出人声部分(300-5000Hz,大部分人声都包含在这个频段内)然后相减,这个差值再和已经滤掉人声部分的左右声道分别进行混合。得出新的左右声道数据。在这里一个最主要的算法也就是离散音频数据的滤波算法,我采用的是简单而快速有效的双二次滤波算法。该算法被封装成一个类:/ Filter1.h: interface for the CFilter class./#if !defined(AFX_FILTER1_H_A8AD654E_7587_41C5_BE3F_D3E266EA5788_INCLUDED_)#define AFX_FILTER1_H_A8AD654E_7587_41C5_BE3F_D3E266EA5788_INCLUDED_#if _MSC_VER 1000#pragma once#endif / _MSC_VER 1000/ 基本双二次滤波器class CFilter public:CFilter();virtual CFilter();/ 设置滤波器参数void SetFilterParament(int frequency, float Q = 1.0f); / 滤波函数virtual void Filter(short * pData, int num, int nch, int srate);protected:/ 计算滤波参数virtual void CalcFilterParament() = 0;int _sr; / 采样频率int _f; / 截止频率float _Q; / 品质因数float _a0, _a1, _a2; / 滤波器参数float _b1, _b2;private:/ 历史值int _xn1_1, _xn2_1, _yn1_1, _yn2_1;int _xn1_2, _xn2_2, _yn1_2, _yn2_2;const float PI = 3.1415926f; / 值/ 截断函数/template_inline int BOUND(int x,int min, int max)return (xmax)?max:x);#endif / !defined(AFX_FILTER1_H_A8AD654E_7587_41C5_BE3F_D3E266EA5788_INCLUDED_)/ Filter1.cpp: implementation of the CFilter class./#include stdafx.h#include Filter1.h#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE=_FILE_;#define new DEBUG_NEW#endif/ Construction/Destruction/CFilter:CFilter()/ 初始化参数_xn1_1 = 0, _xn2_1 = 0, _yn1_1 = 0, _yn2_1 = 0;_xn1_2 = 0, _xn2_2 = 0, _yn1_2 = 0, _yn2_2 = 0;_a0 = 1.0f, _a1 = 1.0f, _a2 = 1.0f;_b1 = 1.0f, _b2 = 1.0f;_sr = 44100;_f = 1000;_Q = 1.0f;CFilter:CFilter()/ 设置滤波参数void CFilter:SetFilterParament(int frequency, float Q)_f = frequency;_Q = Q;CalcFilterParament();/ 滤波函数void CFilter:Filter(short *pData, int num, int nch, int srate)/ 如果采样率改变,应重新计算参数if(srate != _sr)_sr = srate;CalcFilterParament();if(nch = 2) / 双声道short * l = pData;short * r = pData + 1;int i = num;while (i-0)/ 计算输出int lyn = (int)(_a0 * (*l) + _a1 * _xn1_1 + _a2 * _xn2_1- _b1 * _yn1_1 - _b2 * _yn2_1);int ryn = (int)(_a0 * (*r) + _a1 * _xn1_2 + _a2 * _xn2_2- _b1 * _yn1_2 - _b2 * _yn2_2);/ 更新历史数据_xn2_1 = _xn1_1; _xn1_1 = *l;_yn2_1 = _yn1_1; _yn1_1 = lyn;_xn2_2 = _xn1_2; _xn1_2 = *r;_yn2_2 = _yn1_2; _yn1_2 = ryn;/ 截断至16位lyn = BOUND(lyn,-32768,32767);ryn = BOUND(ryn,-32768,32767);*l = lyn;*r = ryn;l+=2;r+=2;else if(nch = 1) / 单声道short * m = pData;int i = num;while (i-0)int myn = (int)(_a0 * (*m) + _a1 * _xn1_1 + _a2 * _xn2_1- _b1 * _yn1_1 - _b2 * _yn2_1);if(myn 32767) myn = 32767;else if(myn 1000#pragma once#endif / _MSC_VER 1000#include Filter1.h/ 高通滤波器class CHighPassFilter : public CFilter public:CHighPassFilter();virtual CHighPassFilter();virtual void CalcFilterParament();#endif / !defined(AFX_HIGHPASSFILTER_H_1A792751_B704_43D5_AEA6_9747710D0AD8_INCLUDED_)/ HighPassFilter.cpp: implementation of the CHighPassFilter class./#include stdafx.h#include HighPassFilter.h#include math.h#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE=_FILE_;#define new DEBUG_NEW#endif/ Construction/Destruction/CHighPassFilter:CHighPassFilter()CHighPassFilter:CHighPassFilter()/ 计算高通滤波器参数void CHighPassFilter:CalcFilterParament()float omega = (2.0f * PI * _f) / _sr;float sin_omega = sinf(omega);float cos_omega = cosf(omega);float alpha = sin_omega / (2.0f * _Q);float scalar = 1.0f / (1.0f + alpha);_a0 = 0.5f * (1.0f + cos_omega) * scalar;_a1 = -(1.0f + cos_omega) * scalar;_a2 = _a0;_b1 = -2.0f * cos_omega * scalar;_b2 = (1.0f - alpha) * scalar;下面这个是低通滤波器类:/ LowPassFilter.h: interface for the CLowPassFilter class./#if !defined(AFX_LOWPASSFILTER_H_5AA8CBCC_1043_4093_B023_C3F4869D7163_INCLUDED_)#define AFX_LOWPASSFILTER_H_5AA8CBCC_1043_4093_B023_C3F4869D7163_INCLUDED_#if _MSC_VER 1000#pragma once#endif / _MSC_VER 1000#include Filter1.h / 低通滤波器class CLowPassFilter : public CFilterpublic:CLowPassFilter();virtual CLowPassFilter();virtual void CalcFilterParament();#endif / !defined(AFX_LOWPASSFILTER_H_5AA8CBCC_1043_4093_B023_C3F4869D7163_INCLUDED_)/ LowPassFilter.cpp: implementation of the CLowPassFilter class./#include stdafx.h#include LowPassFilter.h#include math.h#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE=_FILE_;#define new DEBUG_NEW#endif/ Construction/Destruction/CLowPassFilter:CLowPassFilter()CLowPassFilter:CLowPassFilter()/ 计算低通滤波器参数void CLowPassFilter:CalcFilterParament()float omega = (2.0f * PI * _f) / _sr;float sin_omega = sinf(omega);float cos_omega = cosf(omega);float alpha = sin_omega / (2.0f * _Q);float scalar = 1.0f / (1.0f + alpha);_a0 = 0.5f * (1.0f - cos_omega) * scalar;_a1 = (1.0f - cos_omega) * scalar;_a2 = _a0;_b1 = -2.0f * cos_omega * scalar;_b2 = (1.0f - alpha) * scalar;带通滤波器其实是一个高通滤波器和一个低通滤波器的串联,所以它从这两个类派生:/ BandPassFilter.h: interface for the CBandPassFilter class./#if !defined(AFX_BANDPASSFILTER_H_207E376D_965B_47F9_A8EE_0ED9F14998D6_INCLUDED_)#define AFX_BANDPASSFILTER_H_207E376D_965B_47F9_A8EE_0ED9F14998D6_INCLUDED_#if _MSC_VER 1000#pragma once#endif / _MSC_VER 1000#include HighPassFilter.h#include LowPassFilter.h/ 带通滤波器class CBandPassFilter : public CHighPassFilter, public CLowPassFilter public:CBandPassFilter();virtual CBandPassFilter();/ 滤波函数void Filter(short *pData, int num, int nch, int srate);/ 设置滤波参数void SetFilterParament(int hf, int lf, float hQ = 1.0f, float lQ = 1.0f);#endif / !defined(AFX_BANDPASSFILTER_H_207E376D_965B_47F9_A8EE_0ED9F14998D6_INCLUDED_)/ BandPassFilter.cpp: implementation of the CBandPassFilter class./#include stdafx.h#include filter.h#include BandPassFilter.h#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE=_FILE_;#define new DEBUG_NEW#endif/ Construction/Destruction/CBandPassFilter:CBandPassFilter()CBandPassFilter:CBandPassFilter()/ 滤波函数void CBandPassFilter:Filter(short * pData, int num, int nch, int srate)/ 一个高通和一个低能滤波器串联操作CHighPassFilter:Filter(pData, num, nch, srate);CLowPassFilter:Filter(pData, num, nch, srate);void CBandPassFilter:SetFilterParament(int hf, int lf, float hQ, float lQ)CHighPassFilter:SetFilterParament(hf, hQ);CLowPassFilter:SetFilterParament(lf, lQ);好了,有了这三个滤波器,下面将来构建这个插件DLL:首先,实现DLL的模块导出函数及数据结构:#ifndef _MODULE_H_#define _MODULE_H_#if _MSC_VER 1000#pragma once#endif / _MSC_VER 1000#include dsp.hvoid Config(struct winampDSPModule *this_mod);int Init(struct winampDSPModule *this_mod);void Quit(struct winampDSPModule *this_mod);int DoFilter(struct winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate);#endif / _MODULE_H_头文件中说明了插件用到的几个函数, 这些函数将在其它文件中给出其实现。#include stdafx.h#include module.h#include BandPassFilter.h#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE=_FILE_;#define new DEBUG_NEW#endifwinampDSPModule *GetModule(int which);winampDSPHeader hdr = DSP_HDRVER, 金燕专用效果处理V1.00, GetModule;/ this is the only exported symbol. returns our main header.extern C _declspec( dllexport ) winampDSPHeader *winampDSPGetHeader2()return &hdr;winampDSPModule mod1 =消歌声处理(特别送给金燕),NULL,/ hwndParentNULL,/ hDllInstanceConfig,Init,DoFilter,Quit,NULL;winampDSPModule *GetModule(int index)switch(index)case 0: return &mod1;default:return NULL;由于只包含一个插件,所以GetModule的实现在index为1 的时候返回NULL,告诉WINAMP主程序,仅仅只有一个,下面没有了。我们需要一个对话框来和用户交互:#if !defined(AFX_FILTERDLG_H_F2F4BB76_FB95_4ACE_831B_C13F8E11FD7E_INCLUDED_)#define AFX_FILTERDLG_H_F2F4BB76_FB95_4ACE_831B_C13F8E11FD7E_INCLUDED_#if _MSC_VER 1000#pragma once#endif / _MSC_VER 1000/ FilterDlg.h : header file/#include BandPassFilter.h / CFilterDlg dialog/ 消人声调节对话框class CFilterDlg : public CDialog/ Constructionpublic:CFilterDlg(CWnd* pParent = NULL); / standard constructor/ Dialog Data/AFX_DATA(CFilterDlg)enum IDD = IDD_FILTER ;intm_nch; / 声道数intm_srate; / 采样频率intm_bytes; / 已经处理的字节统计intm_calls; / 处理函数调用次数统计intm_low; / 低频频率intm_high; / 高频频率intm_balance; / 平衡补偿值/AFX_DATABOOL m_bEnable; / 开关BOOL Create(); / 创建对话框void Filter(short* pData, int num, int nch, int srate); / 处理函数/ Overrides/ ClassWizard generated virtual function overrides/AFX_VIRTUAL(CFilterDlg)public:virtual BOOL PreTranslateMessage(MSG* pMsg);virtual BOOL DestroyWindow();protected:virtual void DoDataExchange(CDataExchange* pDX); / DDX/DDV support/AFX_VIRTUAL/ Implementationprotected:CWnd* m_pParentWnd;CBandPassFilter m_filterB; / 带通滤波器CHighPassFilter m_filterH; / 高通滤波器CLowPassFilter m_filterL; / 低能滤波器float m_ls, m_rs;/ Generated message map functions/AFX_MSG(CFilterDlg)afx_msg void OnCheck1();virtual BOOL OnInitDialog();afx_msg void OnDestroy();afx_msg void OnTimer(UINT nIDEvent);afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);/AFX_MSGDECLARE_MESSAGE_MAP();/AFX_INSERT_LOCATION/ Microsoft Visual C+ will insert additional declarations immediately before the previous line.#endif / !defined(AFX_FILTERDLG_H_F2F4BB76_FB95_4ACE_831B_C13F8E11FD7E_INCLUDED_)上面是对话框类的说明, 实现不再列出,请参考完整工程。只给出几个关键的实现:/ 缓冲区short buf165536; / 带通滤波缓冲器short bu

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论