1. 程式人生 > >MFC學習之使用directSound播放音訊檔案

MFC學習之使用directSound播放音訊檔案

環境:windows10+vs2017

音訊格式要求:.wav  (8位24khz)(這是預設的)

directx只能播放wav格式檔案

DirectSound提供了各種音效處理的支援,如:低延遲、3d立體和協調硬體等功能

vs2012開始,vs已經繼承directx

步驟:

  1. 建立DirectSound物件
  2. 設定協作等級(協調使用硬體的許可權)
  3. 建立主快取區
  4. 從音訊檔案載入音訊到次緩衝區
  5. 播放音訊

相關概念:

  • 主快取區: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"庫

此程式碼可以直接執行!

需要注意音訊檔案位置。