啥叫混音呢,其实很简单,如果两个人同时说话 ,他们俩发出的声波在空气中进行了波的叠加,这其实就是个混音。计算机的混音,其实是一个虚拟的混音操作,因为计算机其实是只有一个声源(现在的计算机通常有两声道甚至5声道的立体声,先忽略这些,我们先抽象,把计算机看作一个声源),通过在计算机内部进行运算,把两个波形进行叠加运算,然后由计算机唯一的音箱输出,这就是计算机混音技术。微软的API PlaySound是不支持混音的,调用一个PlaySound的时候,会终止上一个PlaySound调用所播放的声音(异步调用),如果要用PlaySound来实现混音效果,就需要自己写一个混音算法。幸运的是,该叠加算法不需要我们去写,微软的DirectX早已提供给我们了现成的算法,而且非常强大,参与叠加的每个声音分量甚至都能够有自己独立的空间坐标,这也就是3D音效了。我们不需要3d音效,只要能够多路混音就可以了,晚上找到一个开源的对DirectSound的封装,非常好用,现把代码公开如下:
-------------------------------------------------------------------------------------------------
// DSBuffer.h : Definition of CDSBuffer class
//#if !defined(AFX_DSBUFFER_H__7517D749_96E3_11D2_BBF3_9EB4940D843C__INCLUDED_)
#define AFX_DSBUFFER_H__7517D749_96E3_11D2_BBF3_9EB4940D843C__INCLUDED_#if _MSC_VER > 1000
#pragma once#endif // _MSC_VER > 1000#include <mmsystem.h>
#include <dsound.h>struct WaveHeader
{ BYTE RIFF[4]; // "RIFF"DWORD dwSize; // Size of data to followBYTE WAVE[4]; // "WAVE"BYTE fmt_[4]; // "fmt "DWORD dw16; // 16WORD wOne_0; // 1WORD wChnls; // Number of ChannelsDWORD dwSRate; // Sample RateDWORD BytesPerSec; // Sample RateWORD wBlkAlign; // 1WORD BitsPerSample; // Sample sizeBYTE DATA[4]; // "DATA"DWORD dwDSize; // Number of Samples};class CDSBuffer : public CObject
{ // Attributeprotected:LPDIRECTSOUNDBUFFER m_lpDSBuffer; // Sound bufferLPDIRECTSOUND3DBUFFER m_lpDS3DBuffer; // 3D buffer// Construction / Destruction
public:CDSBuffer();CDSBuffer(const char* FileName, LPDIRECTSOUND lpDS, DWORD dwFlags = DSBCAPS_CTRLDEFAULT);~CDSBuffer();// Methods
public:BOOL PlaySound(DWORD dwFlags);BOOL StopSound();BOOL CreateSoundBuffer(LPDIRECTSOUND lpDS, DWORD dwFlags, DWORD dwBufSize, DWORD dwFreq, DWORD dwBitsPerSample, DWORD dwBlkAlign, BOOL bStereo);BOOL ReadData(FILE* pFile, DWORD dwSize, DWORD dwPos);BOOL IsValid();LPDIRECTSOUNDBUFFER GetBuffer() { return m_lpDSBuffer;}LPDIRECTSOUND3DBUFFER Get3DBuffer() { return m_lpDS3DBuffer;}}; #endif // !defined(AFX_DSBUFFER_H__7517D749_96E3_11D2_BBF3_9EB4940D843C__INCLUDED_)-------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------
///
// //// DirectSound Mixer //// //// V1.0 by 1999 //// //// with core inputs from //// Stack-Up //// V1.0 1998 //// //// also //// /////// DSBuffer.cpp : Implementation of CDSBuffer class
//#include "stdafx.h"
#include "DSBuffer.h"#ifdef _DEBUG
#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif/
// CDSBufferCDSBuffer::CDSBuffer()
{ // Reset the sound bufferm_lpDSBuffer = NULL;// Reset the 3D buffer
m_lpDS3DBuffer = NULL;}CDSBuffer::CDSBuffer(const char *FileName, LPDIRECTSOUND lpDS, DWORD dwFlags)
{ // Reset the sound bufferm_lpDSBuffer = NULL;// Reset the 3D buffer
m_lpDS3DBuffer = NULL;// Open the wave file
FILE* pFile = fopen(FileName, "rb");if(!pFile)return;// Read in the wave header
WaveHeader wavHdr;if (fread(&wavHdr, sizeof(wavHdr), 1, pFile) != 1) { fclose(pFile);return;}// Figure out the size of the data region
DWORD dwSize = wavHdr.dwDSize;// Is this a stereo or mono file?
BOOL bStereo = wavHdr.wChnls > 1 ? true : false;// Create the sound buffer for the wave file
if(CreateSoundBuffer(lpDS, dwFlags, dwSize, wavHdr.dwSRate, wavHdr.BitsPerSample, wavHdr.wBlkAlign, bStereo)){ // Read the data for the wave file into the sound bufferif (!ReadData(pFile, dwSize, sizeof(wavHdr)))AfxMessageBox("Error - DS - Reading Data");else if (dwFlags & DSBCAPS_CTRL3D){ // Get pointer to 3D bufferif (S_OK != m_lpDSBuffer->QueryInterface(IID_IDirectSound3DBuffer, (void **)&m_lpDS3DBuffer))AfxMessageBox("DirectSound3DBuffer not available");}}fclose(pFile);
} CDSBuffer::~CDSBuffer(){ StopSound();if(m_lpDSBuffer){ m_lpDSBuffer->Release();}if(m_lpDS3DBuffer)
{ m_lpDS3DBuffer->Release();}} BOOL CDSBuffer::CreateSoundBuffer(LPDIRECTSOUND lpDS, DWORD dwFlags, DWORD dwBufSize, DWORD dwFreq, DWORD dwBitsPerSample, DWORD dwBlkAlign, BOOL bStereo){ PCMWAVEFORMAT pcmwf;DSBUFFERDESC dsbdesc;// Set up wave format structure.
memset( &pcmwf, 0, sizeof(PCMWAVEFORMAT) );pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM; pcmwf.wf.nChannels = bStereo ? 2 : 1;pcmwf.wf.nSamplesPerSec = dwFreq;pcmwf.wf.nBlockAlign = (WORD)dwBlkAlign;pcmwf.wf.nAvgBytesPerSec = pcmwf.wf.nSamplesPerSec * pcmwf.wf.nBlockAlign;pcmwf.wBitsPerSample = (WORD)dwBitsPerSample;// Set up DSBUFFERDESC structure.
memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); // Zero it out. dsbdesc.dwSize = sizeof(DSBUFFERDESC);dsbdesc.dwFlags = dwFlags;dsbdesc.dwBufferBytes = dwBufSize; dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;if (DS_OK != lpDS->CreateSoundBuffer(&dsbdesc, &m_lpDSBuffer, NULL))
{ AfxMessageBox("Error - DS - CreateSoundBuffer");return FALSE;}return TRUE;
} BOOL CDSBuffer::ReadData(FILE* pFile, DWORD dwSize, DWORD dwPos) { // Seek to correct position in file (if necessary)if (dwPos != 0xffffffff) { if (fseek(pFile, dwPos, SEEK_SET) != 0) { return FALSE;}}// Lock data in buffer for writing
LPVOID pData1;DWORD dwData1Size;LPVOID pData2;DWORD dwData2Size;HRESULT rval;rval = m_lpDSBuffer->Lock(0, dwSize, &pData1, &dwData1Size, &pData2, &dwData2Size, DSBLOCK_FROMWRITECURSOR);
if (rval != DS_OK){ return FALSE;}// Read in first chunk of data
if (dwData1Size > 0) { if (fread(pData1, dwData1Size, 1, pFile) != 1) { return FALSE;}}// read in second chunk if necessary
if (dwData2Size > 0) { if (fread(pData2, dwData2Size, 1, pFile) != 1) { return FALSE;}}// Unlock data in buffer
rval = m_lpDSBuffer->Unlock(pData1, dwData1Size, pData2, dwData2Size);if (rval != DS_OK){ return FALSE;}return TRUE;
}BOOL CDSBuffer::PlaySound(DWORD dwFlags)
{ if(m_lpDSBuffer) // Make sure we have a valid sound buffer{ DWORD dwStatus;if (DS_OK != m_lpDSBuffer->GetStatus(&dwStatus)){ AfxMessageBox("Error - DS - GetStatus");return FALSE;}if((dwStatus & DSBSTATUS_PLAYING) != DSBSTATUS_PLAYING)
{ if (DS_OK != m_lpDSBuffer->Play(0, 0, dwFlags)) // Play the sound{ AfxMessageBox("Error - DS - Play");return FALSE;}}}return TRUE;
}BOOL CDSBuffer::StopSound()
{ if(m_lpDSBuffer) // Make sure we have a valid sound buffer{ DWORD dwStatus;if (DS_OK != m_lpDSBuffer->GetStatus(&dwStatus)){ AfxMessageBox("Error - DS - GetStatus");return FALSE;}if ((dwStatus & DSBSTATUS_PLAYING) == DSBSTATUS_PLAYING)
{ if (DS_OK != m_lpDSBuffer->Stop()) // Stop the sound{ AfxMessageBox("Error - DS - Stop");return FALSE;}}
}return TRUE;} BOOL CDSBuffer::IsValid(){ if (m_lpDSBuffer)return TRUE;elsereturn FALSE;}
-------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------
// DSList.h : Definition of CDSList class
//#if !defined(AFX_DSLIST_H__7517D749_96E3_11D2_BBF3_9EB4940D843C__INCLUDED_)
#define AFX_DSLIST_H__7517D749_96E3_11D2_BBF3_9EB4940D843C__INCLUDED_#if _MSC_VER > 1000
#pragma once#endif // _MSC_VER > 1000#include "DSBuffer.h"
class CDSList : virtual protected CObList
{ // Attributeprivate:LPDIRECTSOUND m_lpDS; // DirectSound Object// Construction / Destruction
public:CDSList();~CDSList();// Methods
public:BOOL Init();BOOL StopAllBuffers();BOOL AddNewBuffer(const char* FileName);BOOL RemoveBuffer(int nBuffer);BOOL PlayBuffer(int nBuffer, DWORD dwFlags);BOOL StopBuffer(int nBuffer);LPDIRECTSOUND GetDSObject() {return m_lpDS;}};#endif // !defined(AFX_DSLIST_H__7517D749_96E3_11D2_BBF3_9EB4940D843C__INCLUDED_)
-------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------
///
// //// DirectSound Mixer //// //// V1.0 by 1999 //// //// with core inputs from //// Stack-Up //// V1.0 1998 //// //// also //// /////// DSList.cpp : Implementation of CDSList class
//#include "stdafx.h"
#include "DSList.h"#ifdef _DEBUG
#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif/
// CDSListCDSList::CDSList()
{ // Reset the DirectSound pointerm_lpDS = NULL;} CDSList::~CDSList(){ // Stop all playing buffersStopAllBuffers();if(m_lpDS)
{ m_lpDS->Release();}} BOOL CDSList::Init(){ // Create DirectSound Objectif (DS_OK != DirectSoundCreate(NULL, &m_lpDS, NULL)){ AfxMessageBox("Error - DS - Create/nAudio cannot be used");return FALSE;}// Set Cooperative Level
if (DS_OK != m_lpDS->SetCooperativeLevel(*AfxGetMainWnd(), DSSCL_NORMAL)){ AfxMessageBox("Error - DS - SetCooperativeLevel");return FALSE;}return TRUE;
} BOOL CDSList::AddNewBuffer(const char* FileName){ // Make sure that audio is initializedif (!m_lpDS){ AfxMessageBox("Error - DS - Audio not initialized");return FALSE;}// Try creating the new buffer
CDSBuffer* pNewBuffer = new CDSBuffer(FileName, m_lpDS);// If succesfull add to playlist
if (pNewBuffer->IsValid()){ AddTail(pNewBuffer);return TRUE;}// Else forget it
else{ delete(pNewBuffer);return FALSE;}} BOOL CDSList::RemoveBuffer(int nBuffer){ // Make sure that the buffer index is validif ((nBuffer < 0) || (nBuffer >= GetCount())){ AfxMessageBox("Error - DS - Invalid buffer selection index");return FALSE;}// First stop the buffer
if (StopBuffer(nBuffer)){ // Find the bufferPOSITION Pos = FindIndex(nBuffer);// Remove it
RemoveAt(Pos);return TRUE;}return FALSE;} BOOL CDSList::PlayBuffer(int nBuffer, DWORD dwFlags){ // Make sure that the buffer index is validif ((nBuffer < 0) || (nBuffer >= GetCount())){ AfxMessageBox("Error - DS - Invalid buffer selection index");return FALSE;}// Find the buffer
POSITION Pos = FindIndex(nBuffer);// Retrieve a pointer
CDSBuffer* DSBuffer = (CDSBuffer *)GetAt(Pos);// Try playing it
return DSBuffer->PlaySound(dwFlags);} BOOL CDSList::StopBuffer(int nBuffer){ // Make sure that the buffer index is validif ((nBuffer < 0) || (nBuffer >= GetCount())){ AfxMessageBox("Error - DS - Invalid buffer selection index");return FALSE;}// Find the buffer
POSITION Pos = FindIndex(nBuffer);// Retrieve a pointer
CDSBuffer* DSBuffer = (CDSBuffer *)GetAt(Pos);// Try stopping it
return DSBuffer->StopSound();} BOOL CDSList::StopAllBuffers(){ for (POSITION Pos = GetHeadPosition(); Pos != NULL; ){ CDSBuffer* DSBuffer = (CDSBuffer *)GetNext(Pos);DSBuffer->StopSound();}return TRUE;}
-------------------------------------------------------------------------------------------------
代码使用方法:
1、首先把四个文件添加到工程中。
2、包含相应的头文件,在自己的代码中添加如下代码:
#include "DSList.h"
3、链接DirectSound的相关静态链接库
在自己的代码中加入如下静态链接代码:
//连接LIB库#pragma comment (lib,"winmm.lib")#pragma comment(lib,"dxguid.lib")#pragma comment(lib,"dsound.lib")4、在初始化代码中载入波形文件到缓冲区:
代码示例:
//初始化DirectSound混音器;m_dslist.Init ();m_dslist.AddNewBuffer(GetAppPath()+"//sound//one.wav");m_dslist.AddNewBuffer(GetAppPath()+"//sound//two.wav");m_dslist.AddNewBuffer(GetAppPath()+"//sound//three.wav");5、在需要播放声音的时候调用:m_dslist.PlayBuffer(index,0);
index参数指的是添加缓冲区索引,最先添加的缓冲区索引为0,依此类推。
该函数是异步执行,自动与以前调用该函数所产生的尚未结束的声音混音输出。