以文本方式查看主题 - 计算机科学论坛 (http://bbs.xml.org.cn/index.asp) -- 『 C/C++编程思想 』 (http://bbs.xml.org.cn/list.asp?boardid=61) ---- WinAMP 插件DIY (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=55325) |
-- 作者:卷积内核 -- 发布时间:11/14/2007 11:45:00 AM -- WinAMP 插件DIY 我想:在回答“你用什么播放器听 MP3”这个问题时,90%的人都会回答Winamp!那么你一定用过 Winamp 的插件功能吧,正是多样化的插件使这“老”播放器不断地焕发青春。不管新推出什么音频格式(MP4,VQF,RM...),只要插件一装就能播放。还有形形色色的可视插件,比如Giess 等等,将音乐的节奏感表现的可谓淋漓尽致! 既然插件是用程序编写的,那么我们何不来一试身手,动手做它一个出来?!用过 Winamp 的人都知道,Winamp 插件是放在 Pulgin 文件夹中一个个的 DLL(动态链接库)文件,所以编写 Winamp 插件其实就是编写 Windows 的动态链接库。当然写的时候是要遵循一定的规范的(相关文档可以从 www.winamp.com 下载),在这方面,Winamp 作者 Justin Frankel 写的一个可视插件的例子可以作为我们很好的参考。下面我们就以这个例子(当然也是一个编写规范)为参考,认识一下Winamp 可视插件的编写方法。 (下面的程序可从 Winamp 官方网站下载,文件名为 vis_minisdk.zip) 首先让我们看一下可视插件使用的数据结构(在文件 Vis.h 中) // 注意: typedef struct winampVisModule { // 数据依照各自的 Nch(声道数) 条目被填充 void (*Config)(struct winampVisModule *this_mod); // 模块配置函数 void *userData; // 用户数据 (可选) typedef struct { // 定义导出标识 // 当前模块的版本 (0x101 == 1.01)
上面列出的是一个编写可视插件必须包含的头文件,里面列出了可视插件用到的数据结构。在探讨具体插件程序之前,有一些概念必须搞清:一个可视插件中可以包含若干个模块(每一模块都是一种演示效果,可以在插件选择对话框中选择用哪个模块来演示),这些模块通过某种方法(后面将会看到)被 Winamp 获取,从而得到“表演”的机会。
|
-- 作者:卷积内核 -- 发布时间:11/14/2007 11:46:00 AM -- 简而言之,Winamp 利用所有插件 DLL 中导出的一个统一名称的函数获得了一个插件头数据结构,然后通过此数据结构中的一个函数再去获取各个模块的信息(这个过程与 COM 的 QueryInterface() 用法有些神似,看来好的设计思想是相通的),进而利用多线程(通过 DLL View 观察得知)实现可视插件的展示。下面就是可视插件的源程序: // Winamp 测试用可视插件 v1.0 #include #include "vis.h" char szAppName[] = "SimpleVis"; // 窗口类名 // 有关配置的声明 // 在需要的时候返回一个 winampVisModule,用在下面的 hdr 中。WinAMP 可由此得知插件中的模块数。 // "成员"函数 // 插件窗口的窗口处理函数 // 双缓冲数据 // 第一模块 (示波器) // 第二模块 (光谱分析) // 第三模块 (VU meter) |
-- 作者:卷积内核 -- 发布时间:11/14/2007 11:46:00 AM -- // 这是插件 DLL 中仅有的一个导出函数,用来返回插件头结构体指针,从而进一步得 // 知插件中各个模块的信息。 // 如果你正在编译 C++ 程序,extern "C" { 就是必要的,所以我们使用了 #ifdef。 #ifdef __cplusplus extern "C" { #endif __declspec( dllexport ) winampVisHeader *winampVisGetHeader() { return &hdr; } #ifdef __cplusplus } #endif // 在得到插件头结构体指针后用来获取模块信息。 // 模块配置函数。通过 this_mod 可得知要配置哪个模块。 // 模块初始化函数。注册窗口类,创建窗口等等。 config_read(this_mod); // 读取配置信息 if (!RegisterClass(&wc)) if (!hMainWnd) { // 调整窗口尺寸以使得客户区等于宽乘高 // 显示窗口 // 示波器模块的“表演”函数。成功返回0,如果插件应该被终止则返回1。 // 频谱分析模块的“表演”函数。成功返回0,如果插件应该被终止则返回1。 |
-- 作者:卷积内核 -- 发布时间:11/14/2007 11:46:00 AM -- // VU 表模块的“表演”函数。成功返回0,如果插件应该被终止则返回1。 int render3(struct winampVisModule *this_mod) { int x, y; // 清除背景 Rectangle(memDC,0,0,256,32); // 绘制 VU 表 for (y = 0; y < 2; y ++) { int last=this_mod->waveformData[y][0]; int total=0; for (x = 1; x < 576; x ++) { total += abs(last - this_mod->waveformData[y][x]); last = this_mod->waveformData[y][x]; } total /= 288; if (total > 127) total = 127; if (y) Rectangle(memDC,128,0,128+total,32); else Rectangle(memDC,128-total,0,128,32); } { // 将双缓冲复制到窗口中 HDC hdc = GetDC(hMainWnd); BitBlt(hdc,0,0,256,32,memDC,0,0,SRCCOPY); ReleaseDC(hMainWnd,hdc); } return 0; } // 模块清除函数 (对应于 init())。销毁窗口,取消窗口类的注册。 // 生成一个 \plugin.ini 形式的 .ini 文件名 void config_write(struct winampVisModule *this_mod) config_getinifn(this_mod,ini_file); wsprintf(string,"%d",config_x); 正如我们前面所说,插件程序与主程序之间一定要有相互合作的规范才能正常的运作,比如约定好的函数名等等。从上面的程序中可以看出,Winamp 用约定好的函数名调用插件 DLL 中唯一的一个导出函数 winampVisGetHeader() 来获取一个指向插件头结构体 winampVisHeader 的指针,而后者包含的一个指针函数(*getModule)(int) 就可以根据给定参数向 Winamp 暴露模块的接口。这样一来,插件的全部细节就都出现在 Winamp 面前了。因为 getModule 函数对没有实现的模块返回 NULL,所以 Winamp 就可以通过返回值确定可视插件中模块的数目了。其实上面的程序就是一个可视插件的编写框架,当明确了其中的规范之后就可以把精力放在“表演”函数的编写和具体实现上了。 |
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
77.972ms |