我想:在回答“你用什麼播放器聽 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 中)
// 注意:
// 任何呆在前台的插件窗口都應該將按鍵傳送給其父(WinAMP 的)窗口,以確保
// 用戶仍舊可以控制 WinAMP(除非用戶按了 ESC 鍵或者插件所指定的鍵)。
// 在存儲配置時,配置數據應當統一存放在 plugin.ini 中。
// 請將這個插件例程看作一個框架。
typedef struct winampVisModule {
char *description; // 模塊描述(出現在插件選擇列表框下面得下拉列表框中)
HWND hwndParent; // 父窗口------------- (由主調應用填充)
HINSTANCE hDllInstance; // 此 DLL 的實例句柄 - (由主調應用填充)
int sRate; // 采樣速率 ---------- (由主調應用填充)
int nCh; // 聲道數 ------------ (由主調應用填充)
int latencyMs; // 從調用 RenderFrame 到真正繪制的潛伏時間(毫秒)
// (主調應用在獲取數據的時候會查看這個值)
int delayMs; // 每兩次調用之間的間隔時間(毫秒)
// 數據依照各自的 Nch(聲道數) 條目被填充
int spectrumNch;
int waveformNch;
unsigned char spectrumData[2][576]; // 頻譜數據
unsigned char waveformData[2][576]; // 波形數據
void (*Config)(struct winampVisModule *this_mod); // 模塊配置函數
int (*Init)(struct winampVisModule *this_mod);
// 初始化函數(創建窗口等等)。成功返回0
int (*Render)(struct winampVisModule *this_mod);
// “表演”函數。成功返回0,如返回1表示插件應該終止
void (*Quit)(struct winampVisModule *this_mod);
// 模塊退出函數。完成之後調用
void *userData; // 用戶數據 (可選)
} winampVisModule;
typedef struct {
int version; // VID_HDRVER (當前模塊的版本)
char *description; // 插件的描述(出現在選擇插件對話框的插件列表框中)
winampVisModule* (*getModule)(int); // 用來獲取模塊結構
} winampVisHeader;
// 定義導出標識
typedef winampVisHeader* (*winampVisGetHeaderType)();
// 當前模塊的版本 (0x101 == 1.01)
#define VIS_HDRVER 0x101
上面列出的是一個編寫可視插件必須包含的頭文件,裡面列出了可視插件用到的數據結構。在探討具體插件程序之前,有一些概念必須搞清:一個可視插件中可以包含若干個模塊(每一模塊都是一種演示效果,可以在插件選擇對話框中選擇用哪個模塊來演示),這些模塊通過某種方法(後面將會看到)被 Winamp 獲取,從而得到“表演”的機會。
簡而言之,Winamp 利用所有插件 DLL 中導出的一個統一名稱的函數獲得了一個插件頭數據結構,然後通過此數據結構中的一個函數再去獲取各個模塊的信息(這個過程與 COM 的 QueryInterface() 用法有些神似,看來好的設計思想是相通的),進而利用多線程(通過 DLL View 觀察得知)實現可視插件的展示。下面就是可視插件的源程序:
// Winamp 測試用可視插件 v1.0
// 版權所有 (C) 1997-1998, Justin Frankel/Nullsoft
// 基於此框架可自由的編寫任何可視插件...
#include
#include "vis.h"
char szAppName[] = "SimpleVis"; // 窗口類名
// 有關配置的聲明
int config_x=50, config_y=50; // 窗口在屏幕上的橫縱坐標
void config_read(struct winampVisModule *this_mod); // 讀配置