首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

WinAMP 視覺化插件自製入門:從 DLL 架構到 Render 迴圈完整解析

     編輯:關於C語言

WinAMP 之所以經久不衰,關鍵在於其開放的插件架構——無論新音訊格式(MP4、VQF、RM)還是炫酷視覺化效果(G-Force、MilkDrop),皆透過放置於 Plugins 目錄下的 DLL 實現。本文以官方 SDK 範例 vis_minisdk.zip 為基礎,深入剖析視覺化插件的資料結構、生命週期與繪圖流程,帶你從零打造屬於自己的視覺化模組。

核心資料結構:winampVisModule 與 winampVisHeader

每個視覺化 DLL 必須導出統一名稱的函式 winampVisGetHeader(),傳回 winampVisHeader*,WinAMP 据此發現並載入模組。

typedef struct winampVisModule {
    char *description;        // 模組說明(顯示於插件選單)
    HWND hwndParent;          // 父視窗(WinAMP 主視窗,由主程式填入)
    HINSTANCE hDllInstance;   // DLL 實例句柄(由主程式填入)
    int sRate;                // 取樣率(由主程式填入)
    int nCh;                  // 聲道數(由主程式填入)
    int latencyMs;            // Render 呼叫至實際繪製的延遲(毫秒)
    int delayMs;              // 兩次 Render 間隔(毫秒,控制幀率)

    int spectrumNch;          // 頻譜資料聲道數
    int waveformNch;          // 波形資料聲道數
    unsigned char spectrumData[2][576]; // 頻譜資料 [聲道][頻帶]
    unsigned char waveformData[2][576]; // 波形資料 [聲道][取樣點]

    void (*Config)(struct winampVisModule *);   // 設定對話框
    int  (*Init)(struct winampVisModule *);     // 初始化(建立視窗等),成功傳回 0
    int  (*Render)(struct winampVisModule *);   // 每幀繪製,傳回 1 表示請求卸載
    void (*Quit)(struct winampVisModule *);     // 清理資源

    void *userData;             // 自訂資料指標
} winampVisModule;

typedef struct {
    int version;                // VIS_HDRVER (0x101)
    char *description;          // 插件整體說明
    winampVisModule* (*getModule)(int); // 取得第 N 個模組
} winampVisHeader;

插件發現與載入流程

  1. WinAMP 掃描 Plugins 目錄下所有 *.dll。
  2. 對每個 DLL 呼叫 GetProcAddress("winampVisGetHeader") 取得標頭。
  3. 檢查 version 相容性,讀取 description 顯示於插件清單。
  4. 使用者選擇插件後,WinAMP 呼叫 getModule(0) 取得第一個模組指標。
  5. 呼叫 Init() → 進入多執行緒迴圈反覆呼叫 Render() → 使用者切換/關閉時呼叫 Quit()。

關鍵實作細節

1. Init():建立視窗與資源初始化

int Init(winampVisModule *this_mod) {
    // 讀取儲存的視窗位置
    config_read(this_mod);

    // 註冊視窗類別
    WNDCLASS wc = {0};
    wc.lpfnWndProc = VisWndProc;
    wc.hInstance = this_mod->hDllInstance;
    wc.lpszClassName = szAppName;
    RegisterClass(&wc);

    // 建立視窗(WS_POPUP | WS_VISIBLE,不在工作列顯示)
    this_mod->hwndParent = CreateWindowEx(
        WS_EX_TOPMOST, szAppName, this_mod->description,
        WS_POPUP | WS_VISIBLE, config_x, config_y, 320, 240,
        this_mod->hwndParent, NULL, this_mod->hDllInstance, NULL);

    // 將 this_mod 指標附加至視窗,供 WndProc 使用
    SetWindowLongPtr(this_mod->hwndParent, GWLP_USERDATA, (LONG_PTR)this_mod);

    return 0; // 成功
}

2. Render():高效繪製頻譜/波形

此函式在獨立執行緒中以固定間隔(delayMs)被呼叫,必須極度精簡:

int Render(winampVisModule *this_mod) {
    if (!this_mod->hwndParent) return 0;

    HDC hdc = GetDC(this_mod->hwndParent);
    RECT rc; GetClientRect(this_mod->hwndParent, &rc);

    // 範例:繪製簡單頻譜柱狀圖
    int nCh = this_mod->spectrumNch;
    int bands = 576 / nCh; // 實際有效頻帶數
    int barW = max(1, rc.right / bands);

    for (int ch = 0; ch < nCh; ch++) {
        for (int i = 0; i < bands; i++) {
            int val = this_mod->spectrumData[ch][i];
            int h = (val * rc.bottom) / 255;
            int x = i * barW;
            int y = rc.bottom - h;
            // 依聲道顏色區分
            COLORREF clr = (ch == 0) ? RGB(0,255,0) : RGB(0,0,255);
            HBRUSH br = CreateSolidBrush(clr);
            RECT r = {x, y, x+barW, rc.bottom};
            FillRect(hdc, &r, br);
            DeleteObject(br);
        }
    }
    ReleaseDC(this_mod->hwndParent, hdc);
    return 0; // 繼續下一幀
}

3. Config():模組設定對話框

若需提供使用者設定(如顏色、靈敏度),實作 Config 顯示模態對話框,並將設定寫入 plugin.ini(WinAMP 指定統一設定檔)。

4. 鍵盤訊息轉發

視窗程序必須將未處理的鍵盤訊息轉發給父視窗(WinAMP),確保全域熱鍵(如空白鍵播放/暫停)仍能運作:

LRESULT CALLBACK VisWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    winampVisModule *mod = (winampVisModule*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if (msg == WM_KEYDOWN && (wp == VK_ESCAPE || wp == 'Q')) {
        // ESC 或自訂鍵關閉視覺化
        PostMessage(mod->hwndParent, WM_WA_VIS_END, 0, 0);
        return 0;
    }
    // 其他鍵轉發給 WinAMP
    return SendMessage(mod->hwndParent, msg, wp, lp);
}

編譯與部署

  • 專案類型:Win32 Dynamic-Link Library (DLL)
  • 字元集:Multi-Byte 或 Unicode(WinAMP 早期版本僅支援 ANSI)
  • 輸出檔名:vis_YourName.dll(慣例以 vis_ 開頭)
  • 部署:複製至 WinAMP 安裝目錄下的 Plugins\ 資料夾,重新啟動 WinAMP 即可於 Options → Visualization 選單中看到。

進階主題

  • 雙緩衝繪圖:建立相容 DC + 位圖,離屏繪製後 BitBlt 至螢幕,消除閃爍。
  • OpenGL/Direct3D 加速:在 Init 建立渲染環境,Render 中提交繪圖指令,適合複雜 3D 視覺化。
  • 多模組單一 DLL:一個 DLL 可透過 getModule(index) 提供多種視覺化風格(如「波形」「頻譜」「粒子」),使用者可在下拉選單切換。

總結

WinAMP 視覺化插件開發雖屬經典技術,但其「標準介面 + 多執行緒 Render 迴圈 + 共享記憶體資料交換」的架構思想,對現代音訊視覺化、即時資料儀表板開發仍具參考價值。掌握 winampVisModule 結構與四大回調函式,即可自由發揮創意,打造屬於你的聲音視覺藝術。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved