DirectSound 混音的实现

啥叫混音呢,其实很简单,如果两个人同时说话 ,他们俩发出的声波在空气中进行了波的叠加,这其实就是个混音。计算机的混音,其实是一个虚拟的混音操作,因为计算机其实是只有一个声源(现在的计算机通常有两声道甚至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 follow
BYTE WAVE[4]; // "WAVE"
BYTE fmt_[4]; // "fmt "
DWORD dw16; // 16
WORD wOne_0; // 1
WORD wChnls; // Number of Channels
DWORD dwSRate; // Sample Rate
DWORD BytesPerSec; // Sample Rate
WORD wBlkAlign; // 1
WORD BitsPerSample; // Sample size
BYTE DATA[4]; // "DATA"
DWORD dwDSize; // Number of Samples
};

class CDSBuffer : public CObject
{
// Attribute
protected:
LPDIRECTSOUNDBUFFER m_lpDSBuffer; // Sound buffer
LPDIRECTSOUND3DBUFFER 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 lob_b@hotmail.com 1999 //
// //
// with core inputs from //
// Stack-Up //
// V1.0 ?Tool@theWaterCooler.com 1998 //
// http://www.theWaterCooler.com/Tool //
// also Petr.Stejskal@vslib.cz //
// //
///////////////////////////////////////////////////////////////////////////////

// DSBuffer.cpp : Implementation of CDSBuffer class
//

#include "stdafx.h"
#include "DSBuffer.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDSBuffer

CDSBuffer::CDSBuffer()
{
// Reset the sound buffer
m_lpDSBuffer = NULL;

// Reset the 3D buffer
m_lpDS3DBuffer = NULL;
}

CDSBuffer::CDSBuffer(const char *FileName, LPDIRECTSOUND lpDS, DWORD dwFlags)
{
// Reset the sound buffer
m_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 buffer
if (!ReadData(pFile, dwSize, sizeof(wavHdr)))
AfxMessageBox("Error - DS - Reading Data");
else if (dwFlags & DSBCAPS_CTRL3D)
{
// Get pointer to 3D buffer
if (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;
else
return 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
{
// Attribute
private:
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 lob_b@hotmail.com 1999 //
// //
// with core inputs from //
// Stack-Up //
// V1.0 ?Tool@theWaterCooler.com 1998 //
// http://www.theWaterCooler.com/Tool //
// also Petr.Stejskal@vslib.cz //
// //
///////////////////////////////////////////////////////////////////////////////

// DSList.cpp : Implementation of CDSList class
//

#include "stdafx.h"
#include "DSList.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDSList

CDSList::CDSList()
{
// Reset the DirectSound pointer
m_lpDS = NULL;
}


CDSList::~CDSList()
{
// Stop all playing buffers
StopAllBuffers();

if(m_lpDS)
{
m_lpDS->Release();
}
}


BOOL CDSList::Init()
{
// Create DirectSound Object
if (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 initialized
if (!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 valid
if ((nBuffer < 0) || (nBuffer >= GetCount()))
{
AfxMessageBox("Error - DS - Invalid buffer selection index");
return FALSE;
}

// First stop the buffer
if (StopBuffer(nBuffer))
{
// Find the buffer
POSITION 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 valid
if ((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 valid
if ((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,依此类推。

该函数是异步执行,自动与以前调用该函数所产生的尚未结束的声音混音输出。

原文地址:https://www.cnblogs.com/lzhitian/p/2348601.html