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

VC++ 實作 DirectSound 多聲道混音播放 WAV 檔案完整教學

     編輯:關於VC++

Windows Multimedia API 提供 PlaySound、MCI、低階 Wave API 三種播放方式,但前二者無法同時播放多個 WAV,低階 API 又過於複雜。DirectSound 可同時混音播放 8 個以上 WAV 檔,API 簡單易用,是遊戲與多媒體應用的首選。本文詳細說明從初始化、載入 WAV、建立聲音緩衝區、播放控制到資源釋放的完整流程。

DirectSound 核心步驟

1. 建立與初始化 DirectSound 物件

LPDIRECTSOUND m_pDirectObject;
if (DirectSoundCreate(NULL, &m_pDirectObject, NULL) == DS_OK) {
  // 設定合作層級:DSSCL_NORMAL (一般)、DSSCL_PRIORITY (優先)、DSSCL_EXCLUSIVE (獨佔)
  m_pDirectObject->SetCooperativeLevel(m_hWnd, DSSCL_NORMAL);
} else {
  AfxMessageBox(_T("DirectSound 建立失敗"));
}

2. 讀取 WAV 檔案至記憶體,解析 RIFF 結構

WAV 檔為 RIFF 格式,包含 fmt (格式區塊) 與 data (資料區塊)。

BOOL CDirectWave::LoadFile(CString Filename) {
  CFile File;
  if (!File.Open(Filename, CFile::modeRead | CFile::shareDenyNone)) return FALSE;
  DWORD dwSize = File.Seek(0, CFile::end);
  File.Seek(0, CFile::begin);
  m_pMemory = GlobalAlloc(GMEM_FIXED, dwSize);
  File.ReadHuge(m_pMemory, dwSize);
  File.Close();

  LPDWORD pdw = (LPDWORD)m_pMemory;
  DWORD dwRiff = *pdw++; // 'RIFF'
  DWORD dwLength = *pdw++;
  DWORD dwType = *pdw++; // 'WAVE'
  if (dwRiff != mmioFOURCC('R','I','F','F') || dwType != mmioFOURCC('W','A','V','E')) return FALSE;

  LPDWORD pdwEnd = (LPDWORD)((BYTE*)pdw + dwLength - 4);
  while (pdw < pdwEnd) {
    DWORD dwChunkType = *pdw++;
    DWORD dwChunkSize = *pdw++;
    switch (dwChunkType) {
      case mmioFOURCC('f','m','t',' '): // 格式區塊
        if (!m_pFormat && dwChunkSize >= sizeof(WAVEFORMAT))
          m_pFormat = (LPWAVEFORMATEX)pdw;
        break;
      case mmioFOURCC('d','a','t','a'): // 資料區塊
        if (!m_pData) {
          m_pData = (LPBYTE)pdw;
          m_dwSize = dwChunkSize;
        }
        break;
    }
    pdw = (LPDWORD)((BYTE*)pdw + ((dwChunkSize + 1) & ~1)); // 字對齊
  }
  return (m_pFormat && m_pData);
}

3. 建立聲音緩衝區

DSBUFFERDESC BufferDesc = {0};
BufferDesc.dwSize = sizeof(DSBUFFERDESC);
BufferDesc.lpwfxFormat = m_pFormat;
BufferDesc.dwBufferBytes = m_dwSize;
BufferDesc.dwFlags = 0; // 靜態緩衝區
if (m_pDirectObject->CreateSoundBuffer(&BufferDesc, &m_pDSoundBuffer, NULL) != DS_OK)
  return FALSE;

4. 載入聲音資料至緩衝區

BOOL CDirectWave::LoadData() {
  LPVOID lpPtr1, lpPtr2; DWORD dwLen1, dwLen2;
  HRESULT hr = m_pDSoundBuffer->Lock(0, m_dwSize, &lpPtr1, &dwLen1, &lpPtr2, &dwLen2, 0);
  if (hr == DS_OK) {
    memcpy(lpPtr1, m_pData, dwLen1);
    if (lpPtr2) memcpy(lpPtr2, m_pData + dwLen1, dwLen2);
    m_pDSoundBuffer->Unlock(lpPtr1, dwLen1, lpPtr2, dwLen2);
    return TRUE;
  } else if (hr == DSERR_BUFFERLOST) {
    m_pDSoundBuffer->Restore();
    return LoadData(); // 遞迴重試
  }
  return FALSE;
}

5. 播放、停止與循環控制

void CDirectWave::Play(BOOL bLoop) {
  DWORD dwFlags = bLoop ? DSBPLAY_LOOPING : 0;
  HRESULT hr = m_pDSoundBuffer->Play(0, 0, dwFlags);
  if (hr == DSERR_BUFFERLOST) {
    if (LoadData()) Play(bLoop); // 重新載入後重試
  }
}

void CDirectWave::Stop() {
  m_pDSoundBuffer->Stop();
}

6. 釋放資源

// 解構子或明確釋放
if (m_pDSoundBuffer) { m_pDSoundBuffer->Release(); m_pDSoundBuffer = NULL; }
if (m_pDirectObject) { m_pDirectObject->Release(); m_pDirectObject = NULL; }
if (m_pMemory) { GlobalFree(m_pMemory); m_pMemory = NULL; }

必要標頭檔與庫

  • #include <mmsystem.h>
  • #include <dsound.h>
  • 連結 dsound.lib、winmm.lib

限制與注意事項

  • 僅支援 Windows 95/98/Me/NT4/2000/XP (需 DirectX 3.0 以上);Vista 之後建議改用 XAudio2 / WASAPI。
  • 不支援 WAV 錄製與儲存,需搭配 WaveIn API 或 Media Foundation。
  • 緩衝區遺失 (DSERR_BUFFERLOST) 必須處理 Restore() 並重新載入資料。
  • 大量同時播放時注意聲道混音數量限制 (硬體相關)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved