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

VC++ 應用程式播放聲音資源檔:多執行緒 + FindResource/LoadResource/LockResource + sndPlaySound

     編輯:VC 教學

需求場景

應用程式啟動時播放歡迎音效、操作完成提示音、錯誤警示音。將 .wav 檔內嵌為資源,避免外部檔案遺失、部署麻煩。播放過程不阻塞主執行緒 UI 回應。

資源嵌入步驟

  1. 準備 .wav 檔 (如 notify.wav)。
  2. Visual Studio 方案總管 → 右鍵專案 → Add Resource → Import → 選擇 .wav → 類型輸入 WAVE (大寫) → ID 自動指派 (如 IDR_WAVE1)。
  3. 資源檔 (.rc) 會出現:IDR_WAVE1 WAVE "notify.wav"。

核心播放流程:資源鎖定 → 記憶體播放


// 1. 取得模組句柄
HINSTANCE hInst = AfxGetInstanceHandle();

// 2. 尋找資源
HRSRC hRsrc = FindResource(hInst, MAKEINTRESOURCE(IDR_WAVE1), _T("WAVE"));
if (!hRsrc) return; // 找不到

// 3. 載入資源至全域記憶體
HGLOBAL hGlobal = LoadResource(hInst, hRsrc);
if (!hGlobal) return;

// 4. 鎖定記憶體取得指標
LPVOID pSoundData = LockResource(hGlobal);
if (!pSoundData) return;

// 5. 取得資源大小 (可選,sndPlaySound 不需大小)
DWORD dwSize = SizeofResource(hInst, hRsrc);

// 6. 播放 (SND_MEMORY: 從記憶體 | SND_ASYNC: 非同步 | SND_NODEFAULT: 找不到不播放預設音)
sndPlaySound((LPCSTR)pSoundData, SND_MEMORY | SND_ASYNC | SND_NODEFAULT);

// 7. 釋放資源 (非同步播放時需確保資源存活到播放結束,通常配合回呼或延遲釋放)
// 簡單做法:同步播放 (SND_SYNC) 直接 FreeResource
// 非同步做法:另開執行緒播放完再釋放

完整實作:非同步播放執行緒封裝類

CAsyncSoundPlayer.h


#pragma once
#include 

class CAsyncSoundPlayer
{
public:
    static void PlayResource(UINT nResourceID, LPCTSTR lpszType = _T("WAVE"));
    static void PlayFile(LPCTSTR lpszFilePath);

private:
    static UINT __cdecl PlayThread(LPVOID pParam);

    struct ThreadParam
    {
        HINSTANCE hInst;
        UINT nResID;
        LPCTSTR lpszType;
        CString strFile; // 檔案播放用
        bool bIsFile;
    };
};

CAsyncSoundPlayer.cpp


#include "stdafx.h"
#include "AsyncSoundPlayer.h"
#include 
#pragma comment(lib, "winmm.lib")

UINT __cdecl CAsyncSoundPlayer::PlayThread(LPVOID pParam)
{
    ThreadParam* p = (ThreadParam*)pParam;
    if (!p) return 1;

    if (p->bIsFile)
    {
        // 檔案播放
        sndPlaySound(p->strFile, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
        // 檔案播放不需鎖定資源,但需確保字串存活
        // 簡單做法:Sleep 估算播放時間,或使用波形音訊 API 精確控制
        Sleep(2000); // 粗略等待
    }
    else
    {
        // 資源播放
        HRSRC hRsrc = FindResource(p->hInst, MAKEINTRESOURCE(p->nResID), p->lpszType);
        if (hRsrc)
        {
            HGLOBAL hGlobal = LoadResource(p->hInst, hRsrc);
            if (hGlobal)
            {
                LPVOID pData = LockResource(hGlobal);
                if (pData)
                {
                    sndPlaySound((LPCSTR)pData, SND_MEMORY | SND_SYNC | SND_NODEFAULT);
                    // SND_SYNC 會阻塞到播放結束,安全釋放
                }
                FreeResource(hGlobal);
            }
        }
    }

    delete p; // 釋放參數結構
    return 0;
}

void CAsyncSoundPlayer::PlayResource(UINT nResourceID, LPCTSTR lpszType)
{
    ThreadParam* p = new ThreadParam;
    p->hInst = AfxGetInstanceHandle();
    p->nResID = nResourceID;
    p->lpszType = lpszType;
    p->bIsFile = false;

    AfxBeginThread(PlayThread, p, THREAD_PRIORITY_NORMAL);
}

void CAsyncSoundPlayer::PlayFile(LPCTSTR lpszFilePath)
{
    ThreadParam* p = new ThreadParam;
    p->strFile = lpszFilePath;
    p->bIsFile = true;

    AfxBeginThread(PlayThread, p, THREAD_PRIORITY_NORMAL);
}

使用範例


// View 左鍵點擊播放資源音效
void CMyView::OnLButtonDown(UINT nFlags, CPoint point)
{
    CAsyncSoundPlayer::PlayResource(IDR_WAVE1); // 播放嵌入資源
    CView::OnLButtonDown(nFlags, point);
}

// 工具列按鈕播放外部檔案
void CMainFrame::OnPlayExternalSound()
{
    CAsyncSoundPlayer::PlayFile(_T("C:\\Sounds\\alert.wav"));
}

進階:使用 Windows Multimedia API (waveOut) 實現精確控制

sndPlaySound 功能有限 (無音量控制、無播放完成通知、無混音)。專業音訊應用建議使用 waveOutOpen/waveOutWrite/waveOutClose 系列 API,或更高層的 DirectSound / XAudio2 / WASAPI。

關鍵差異對照

特性sndPlaySoundwaveOut APIDirectSound/XAudio2
API 層級高層封裝底層波形音訊現代音訊引擎
非同步支援SND_ASYNC (簡易)回呼函式/事件完整非同步管線
音量控制無waveOutSetVolume完整混音器
多聲道混音無 (互相打斷)手動混音硬體加速混音
格式支援僅 PCM WAVPCM WAVWAV, MP3, ADPCM, xWMA...
適用場景簡單提示音語音錄製/播放、簡單混音遊戲、多媒體、專業音訊

常見問題排查

  • 無聲音:確認資源類型為 WAVE (大寫)、ID 正確、.wav 格式為 PCM (非 ADPCM/MP3)。
  • 播放卡頓:大型 WAV 檔鎖定記憶體時間過長,建議改用 waveOut 串流播放。
  • 非同步播放重疊衝突:sndPlaySound 單一執行個體,同時呼叫會互相打斷。需自行管理混音佇列或升級 API。
  • 64 位元相容:sndPlaySound 參數為 LPCSTR (32 位元指標),64 位元編譯需確保指標截斷正確,建議改用 PlaySound (支援 LPCVOID)。

結語

內嵌聲音資源 + 非同步執行緒播放,是 MFC 應用程式加入音效回饋的輕量級方案。掌握 FindResource → LoadResource → LockResource → sndPlaySound(SND_MEMORY) 流程,即可輕鬆實現。對於音量控制、混音、格式支援有更高需求時,請果斷遷移至 XAudio2 或 WASAPI 現代音訊架構。

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