程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> DirectSound操作WAVE文件的方法

DirectSound操作WAVE文件的方法

編輯:C++入門知識

DirectSound操作WAVE文件的方法
  MCI雖然調用簡單,功能強大,可以滿足聲音文件處理的基本需要,但是MCI也有它的缺點,那就是它一次只能播放一個WAVE文件,有時在實際應用中,為了實現混音效果,需要同時播放兩個或兩個以上的WAVE文件時,就需要使用微軟DirectX技術中的DirectSound了,該技術直接操作底層聲卡設備,可以實現八個以上WAV文件的同時播放。

  實現DirectSound需要以下幾個步驟:1.創建及初始化DirectSound;2.設定應用程序的聲音設備優先級別方式,一般為DSSCL_NORMAL;2. 將WAV文件讀入內存,找到格式塊、數據塊位置及數據長度;3.創建聲音緩沖區;4.載入聲音數據;5.播放及停止:

  下面的函數利用DirectSound技術實現了一個WAVE聲音文件的播放(注意項目設置中要包含"dsound.lib、dxguid.lib"的內容),代碼和注釋如下:

void CPlaysoundView::OnPlaySound()
{
 // TODO: Add your command handler code here
 LPVOID lpPtr1;//指針1;
 LPVOID lpPtr2;//指針2;
 HRESULT hResult;
 DWORD dwLen1,dwLen2;
 LPVOID m_pMemory;//內存指針;
 LPWAVEFORMATEX m_pFormat;//LPWAVEFORMATEX變量;
 LPVOID m_pData;//指向語音數據塊的指針;
 DWORD m_dwSize;//WAVE文件中語音數據塊的長度;
 CFile File;//Cfile對象;
 DWORD dwSize;//存放WAV文件長度;
 //打開sound.wav文件;
 if (!File.Open ("d://sound.wav", CFile::modeRead |CFile::shareDenyNone))
  return ;
 dwSize = File.Seek (0, CFile::end);//獲取WAVE文件長度;
 File.Seek (0, CFile::begin);//定位到打開的WAVE文件頭;
 //為m_pMemory分配內存,類型為LPVOID,用來存放WAVE文件中的數據;
 m_pMemory = GlobalAlloc (GMEM_FIXED, dwSize);
 if (File.ReadHuge (m_pMemory, dwSize) != dwSize)//讀取文件中的數據;
 {
  File.Close ();
  return ;
 }
 File.Close ();
 LPDWORD pdw,pdwEnd;
 DWORD dwRiff,dwType, dwLength;
 if (m_pFormat) //格式塊指針
  m_pFormat = NULL;
 if (m_pData) //數據塊指針,類型:LPBYTE
  m_pData = NULL;
 if (m_dwSize) //數據長度,類型:DWORD
  m_dwSize = 0;
 pdw = (DWORD *) m_pMemory;
 dwRiff = *pdw++;
 dwLength = *pdw++;
 dwType = *pdw++;
 if (dwRiff != mmioFOURCC ('R', 'I', 'F', 'F'))
  return ;//判斷文件頭是否為"RIFF"字符;
 if (dwType != mmioFOURCC ('W', 'A', 'V', 'E'))
  return ;//判斷文件格式是否為"WAVE";
 //尋找格式塊,數據塊位置及數據長度
 pdwEnd = (DWORD *)((BYTE *) m_pMemory+dwLength -4);
 bool m_bend=false;
 while ((pdw < pdwEnd)&&(!m_bend))
 //pdw文件沒有指到文件末尾並且沒有獲取到聲音數據時繼續;
 {
  dwType = *pdw++;
  dwLength = *pdw++;
  switch (dwType)
  {
   case mmioFOURCC('f', 'm', 't', ' ')://如果為"fmt"標志;
    if (!m_pFormat)//獲取LPWAVEFORMATEX結構數據;
    {
     if (dwLength < sizeof (WAVEFORMAT))
      return ;
     m_pFormat = (LPWAVEFORMATEX) pdw;
 
    }
    break;
   case mmioFOURCC('d', 'a', 't', 'a')://如果為"data"標志;
    if (!m_pData || !m_dwSize)
    {
     m_pData = (LPBYTE) pdw;//得到指向聲音數據塊的指針;
     m_dwSize = dwLength;//獲取聲音數據塊的長度;
     if (m_pFormat)
      m_bend=TRUE;
    }
    break;
  }
  pdw = (DWORD *)((BYTE *) pdw + ((dwLength + 1)&~1));//修改pdw指針,繼續循環;

 }
 DSBUFFERDESC BufferDesc;//定義DSUBUFFERDESC結構對象;
 memset (&BufferDesc, 0, sizeof (BufferDesc));
 BufferDesc.lpwfxFormat = (LPWAVEFORMATEX)m_pFormat;
 BufferDesc.dwSize = sizeof (DSBUFFERDESC);
 BufferDesc.dwBufferBytes = m_dwSize;
 BufferDesc.dwFlags = 0;
 HRESULT hRes;
 LPDIRECTSOUND m_lpDirectSound;
 hRes = ::DirectSoundCreate(0, &m_lpDirectSound, 0);//創建DirectSound對象;
 if( hRes != DS_OK )
  return;
 m_lpDirectSound->SetCooperativeLevel(this->GetSafeHwnd(), DSSCL_NORMAL);
 //設置聲音設備優先級別為"NORMAL";
 //創建聲音數據緩沖;
 LPDIRECTSOUNDBUFFER m_pDSoundBuffer;
 if (m_lpDirectSound->CreateSoundBuffer (&BufferDesc, &m_pDSoundBuffer, 0) == DS_OK)
  //載入聲音數據,這裡使用兩個指針lpPtr1,lpPtr2來指向DirectSoundBuffer緩沖區的數據,這是為了處理大型WAVE文件而設計的。dwLen1,dwLen2分別對應這兩個指針所指向的緩沖區的長度。

  hResult=m_pDSoundBuffer->Lock(0,m_dwSize,&lpPtr1,&dwLen1,&lpPtr2,&dwLen2,0);
 if (hResult == DS_OK)
 {
  memcpy (lpPtr1, m_pData, dwLen1);
  if(dwLen2>0)
  {
   BYTE *m_pData1=(BYTE*)m_pData+dwLen1;
   m_pData=(void *)m_pData1;
   memcpy(lpPtr2,m_pData, dwLen2);
  }
  m_pDSoundBuffer->Unlock (lpPtr1, dwLen1, lpPtr2, dwLen2);

 }
 DWORD dwFlags = 0;
 m_pDSoundBuffer->Play (0, 0, dwFlags); //播放WAVE聲音數據;
}

  為了更好的說明DiretSound編程的實現,筆者使用了一個函數來實現所有的操作,當然讀者可以將上面的內容包裝到一個類中,從而更好的實現程序的封裝性,至於如何實現就不需要筆者多說了,真不明白的話,找本C++的書看看。如果定義了類,那麼就可以一次聲明多個對象來實現多個WAVE聲音文件的混合播放。也許細心的讀者會發現,在介紹WAVE文件格式的時候我們介紹了PCMWAVEFORMAT結構,但是在代碼的實現讀取WAVE文件數據部分,我們使用的卻是LPWAVEFORMATEX結構,那末是不是我們有錯誤呢?其實沒有錯,對於PCM格式的WAVE文件來說,這兩個結構是完全一樣的,使用LPWAVEFORMATEX結構不過是為了方便設置DSBUFFERDESC對象罷了。

  操作WAVE聲音文件的方法很多,靈活的運用它們可以靈活地操作WAVE文件,這些函數的詳細用途讀者可以參考MSDN。本文只是對WAVE文件的操作作了一個膚淺的介紹,希望可以對讀者起到拋磚引玉的作用。

摘自 yum2006的專欄
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved