(四)MFC學習之使用directSound播放音訊檔案
阿新 • • 發佈:2018-11-11
環境:windows10+vs2017
音訊格式要求:.wav (8位24khz)(這是預設的)
directx只能播放wav格式檔案
DirectSound提供了各種音效處理的支援,如:低延遲、3d立體和協調硬體等功能
vs2012開始,vs已經整合directx
步驟:
- 建立DirectSound物件
- 設定協作等級(協調使用硬體的許可權)
- 建立主快取區
- 從音訊檔案載入音訊到次緩衝區
- 播放音訊
相關概念:
- 主快取區:DiectSound播放聲音、產生混音效果的區域。預設播放格式是(8bit,22kHz)如果不需要播放其他格式(指的是不同位,不同頻率的音訊,但是都是wav格式),就不需要手動建立主快取區,否則需要先建立主緩衝區(16bit,44kHz),對其格式進行設定。(相當於舞臺)
- 次緩衝區:主要用於儲存要播放的音訊資料及播放格式,可以建立數個次緩衝區來存放多個要播放的聲音(相當於舞臺幕後)
示意圖:
WAVE音訊檔案格式:
是RIFF檔案(Resource InterChange File Format ),DirectSound只接受“.wav”檔案
使用到的類:
窗體程式基本就用到CMyApp,CMyWnd
其餘就是:
LPDIRECTSOUND;
LPDIRECTSOUNDBUFFER ;
HRESULT
程式碼:
#include <afxwin.h> #include <mmsystem.h> #include <dsound.h> //常量定義 //#define SOUND_FILE_NAME "SOUND.WAV"; //執行緒部分 HANDLE threadHandle; DWORD threadId; DWORD funcRun(LPVOID lParam); class CMyWnd :public CFrameWnd { private: CDC *mdc; CBitmap* bmp[2]; //優先順序最高 [] //優先順序最低 , // CRect rect; CRect * rect; int mFrameNo; int x; //聲音部分 LPDIRECTSOUND pDs; LPDIRECTSOUNDBUFFER pDSB_BK, pDSB_MG; HRESULT result; public: CMyWnd(); LPDIRECTSOUNDBUFFER CreateSecondBuffer(LPSTR filename); DECLARE_MESSAGE_MAP() afx_msg void OnPaint(); }; CMyWnd::CMyWnd() { Create(NULL, "TestThread"); mdc = new CDC; CClientDC dc(this); mFrameNo = 0; //客戶區大小 rect = new CRect; GetClientRect(rect); x = 63; //建立執行緒 threadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)funcRun, this, 0, &threadId); mdc->CreateCompatibleDC(&dc); for (int i = 0; i < 2; i++) { bmp[i] = new CBitmap; } //springf_s; bmp[0]->m_hObject = LoadImage(NULL, "bground.bmp", IMAGE_BITMAP, rect->right, rect->bottom, LR_LOADFROMFILE); bmp[1]->m_hObject = LoadImage(NULL, "crimer1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //1,建立directSound //音訊裝置指標, 第三個必須為NULL, result = DirectSoundCreate(NULL, &pDs, NULL); if (result != DS_OK) { MessageBox(_T("建立DirectSound失敗!")); } //2,設定程式協作等級 result = pDs->SetCooperativeLevel(m_hWnd, DSSCL_PRIORITY); if (result != DS_OK) { MessageBox(_T("設定程式協調等級失敗")); } //載入背景音樂 char szMusicName[40]; sprintf_s(szMusicName, "wavs/jump.wav"); /*CString fileName = "wavs/bgm.wav";*/ pDSB_BK = CreateSecondBuffer(szMusicName); pDSB_BK->Play(0, 0, 1); } //建立次緩衝區 LPDIRECTSOUNDBUFFER CMyWnd:: CreateSecondBuffer(LPSTR filename) { //聲音部分 HMMIO hmmio; MMRESULT mmresult;//函式返回值 //1,開啟音訊檔案 hmmio = mmioOpen(filename, NULL, MMIO_ALLOCBUF | MMIO_READ); if (NULL == hmmio) { MessageBox(_T("檔案開啟失敗")); } // 2,確認是否為riff檔案 MMCKINFO ckriff;//WAVE檔案資訊結構 ckriff.fccType = mmioFOURCC('W', 'A', 'V', 'E'); mmresult = mmioDescend(hmmio, &ckriff, NULL, MMIO_FINDRIFF);//最後一個常量是查詢型別 if (MMSYSERR_NOERROR != mmresult) { MessageBox(_T("檔案型別不是wave")); } //3,進入fmt區塊,讀取音訊格式,然後退出fmt區塊 MMCKINFO ckinfo; ckinfo.ckid = mmioFOURCC('f', 'm', 't', ' ');//設定區塊型別 mmresult = mmioDescend(hmmio, &ckinfo, &ckriff, MMIO_FINDCHUNK);//此方法退出區塊 if (MMSYSERR_NOERROR != mmresult) { MessageBox(_T("進入fmt區域錯誤")); } WAVEFORMATEX swfmt; //讀取音訊格式 if (-1 == mmioRead(hmmio, (HPSTR)&swfmt, sizeof(swfmt))) { MessageBox("讀取音訊檔案格式失敗"); } //4,進入data區塊,讀取音訊長度 mmioAscend(hmmio, &ckinfo, 0); ckinfo.ckid = mmioFOURCC('d','a','t', 'a'); mmresult = mmioDescend(hmmio, &ckinfo, &ckriff, MMIO_FINDCHUNK); if (MMSYSERR_NOERROR != mmresult) { MessageBox("進入data區域失敗"); } //5,建立音訊次緩衝區 LPDIRECTSOUNDBUFFER pTempBuf ; DSBUFFERDESC desc;//用以描述緩衝區結構 memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(desc); desc.lpwfxFormat = &swfmt; desc.dwFlags = DSBCAPS_STATIC;//static表示可多次播放,當然還可以指定其他的使用| 連線 desc.dwBufferBytes = ckinfo.cksize; result = pDs->CreateSoundBuffer(&desc, &pTempBuf, NULL); if (DS_OK != result) { MessageBox("建立次緩衝失敗"); return NULL; } // 6,從檔案讀取音訊資料存入次緩衝區 LPVOID pAudio; DWORD BytesAudio; pTempBuf->Lock(0, ckinfo.cksize, &pAudio, &BytesAudio, NULL, NULL, NULL); if (-1 == mmioRead(hmmio, (HPSTR)pAudio, BytesAudio)) { MessageBox("讀取音訊資料失敗"); } pTempBuf->Unlock(pAudio, BytesAudio, NULL, NULL); //7,關閉檔案 mmioClose(hmmio, 0); return pTempBuf; } class CMyApp :public CWinApp { BOOL InitInstance(); }; BOOL CMyApp::InitInstance() { CMyWnd * pf = new CMyWnd; // pf->Create(0,"hello"); pf->ShowWindow(m_nCmdShow); pf->UpdateWindow(); this->m_pMainWnd = pf; return TRUE; } BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd) ON_WM_PAINT() END_MESSAGE_MAP() void CMyWnd::OnPaint() { //這裡只能使用CPaintDc CPaintDC dc(this); // device context for painting // TODO: 在此處新增訊息處理程式程式碼 // 不為繪圖訊息呼叫 CFrameWnd::OnPaint() mdc->SelectObject(bmp[0]); dc.BitBlt(0, 0, rect->right, rect->bottom, mdc, 0, 0, SRCCOPY); mdc->SelectObject(bmp[1]); switch (mFrameNo) { case 0: dc.BitBlt(x, rect->bottom/2-70,49, 154, mdc,49, 0, SRCAND); dc.BitBlt(x, rect->bottom/2-70, 49, 154, mdc, 0, 0, SRCPAINT); mFrameNo = 1; break; case 1: dc.BitBlt(x, rect->bottom /2-70, 49, 154, mdc, 49, 154, SRCAND); dc.BitBlt(x, rect->bottom /2-70, 49, 154, mdc, 0, 154, SRCPAINT); mFrameNo =0; break; default: break; } x += 63; if (x > rect->right) { x = 63; } } //執行緒方法實現 DWORD funcRun(LPVOID lParam) { CMyWnd* wnd = (CMyWnd*)lParam; while (1) { wnd->Invalidate(); Sleep(500); } return 0; } CMyApp TheApp;
注意使用directsound需要注意依賴庫:
- 標頭檔案:“mmsystem.h”和“sdound.h”
- 專案->屬性->連結器->附加依賴項中新增:“dxguid.lib”、“dsound.lib”、"winmm.lib"庫
此程式碼可以直接執行!
需要注意音訊檔案位置。
wav聲音製作:
使用軟體:Cool Edit pro 即可,使用方便(附上網盤連結 )
連結:https://pan.baidu.com/s/1DgLTYgSBwgq-T0HP-1dxCw
提取碼:l886
以將mp3轉為 8位,22khz的wav檔案為例:
file-》open(選擇mp3檔案-》edit-》convert ...->設定一些引數-》等待幾秒即可完成轉換-》file->save as(選擇wav格式儲存即可)
設定引數: