Sound,在Programming.Role.Playing.Games书上,有介绍到使用DirectMusic和DirectSound,但dx9实在是太老了,DirectMusic也已经不支持了,而且现在还有个XAudio2组件,所以书上这一章节其实没有大多价值。
大致流程跟之前的DirectInput类似,创建设备(CreateDevice),设置协作级(SetCooperativeLevel),创建一个主缓冲对象(CreateSoundBuffer),创建副缓冲对象(QueryInterface(IID_IDirectSoundBuffer8,..),可以多个,用于混音),创建通知对象(QueryInterface(IID_IDirectSoundNotify,..)),设置通知位置(SetNotificationPositions),数据填充到副缓冲区(Lock,Unlock),开始播放(Play),其实也可以不设置通知的。详细的可以看下这篇,写的还是挺详细的https://www.cnblogs.com/lidabo/p/4160057.html。
没有用书上的代码,用的是之前学习DX11的DirectSound代码,没有设置通知,所以相对简单点。加了混音,用的是这两首歌さくら - あなたに出会えてよかった和百恋歌,使用千千静听转的wav格式。
#include "SoundClass.h"
SoundClass::SoundClass()
{
m_sound = nullptr;
m_primaryBuffer = nullptr;
m_secondaryBuffer = nullptr;
}
SoundClass::SoundClass(const SoundClass& other)
{
}
SoundClass::~SoundClass()
{
}
bool SoundClass::Initialize(HWND hwnd)
{
bool result;
result = InitializeDirectSound(hwnd);
if (!result)
return false;
result = LoadWaveFile("Sound/Test.wav", &m_secondaryBuffer);
if (!result)
return false;
result = LoadWaveFile("Sound/Test2.wav", &m_thirdBuffer);
if (!result)
return false;
result = PlayWaveFile(&m_secondaryBuffer);
if (!result)
return false;
result = PlayWaveFile(&m_thirdBuffer);
if (!result)
return false;
return true;
}
void SoundClass::Shutdown()
{
ShutdownWaveFile(&m_secondaryBuffer);
ShutdownDirectSound();
}
void SoundClass::Play()
{
bool result;
result = PlayWaveFile(&m_secondaryBuffer);
if (!result)
return;
result = PlayWaveFile(&m_thirdBuffer);
if (!result)
return;
}
void SoundClass::Stop()
{
m_secondaryBuffer->Stop();
m_thirdBuffer->Stop();
}
bool SoundClass::InitializeDirectSound(HWND hwnd)
{
HRESULT result;
DSBUFFERDESC bufferDesc;
WAVEFORMATEX waveFormat;
result = ::DirectSoundCreate8(nullptr, &m_sound, nullptr);
if (FAILED(result))
return false;
result = m_sound->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
if (FAILED(result))
return false;
// Setup the primary buffer description
::memset(&bufferDesc, 0, sizeof(DSBUFFERDESC));
bufferDesc.dwSize = sizeof(DSBUFFERDESC);
bufferDesc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
bufferDesc.dwBufferBytes = 0;
bufferDesc.dwReserved = 0;
bufferDesc.lpwfxFormat = nullptr;
bufferDesc.guid3DAlgorithm = GUID_NULL;
result = m_sound->CreateSoundBuffer(&bufferDesc, &m_primaryBuffer, nullptr);
if (FAILED(result))
return false;
// Setup the format of the primary sound buffer
::memset(&waveFormat, 0, sizeof(WAVEFORMATEX));
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nSamplesPerSec = 44100;
waveFormat.wBitsPerSample = 16;
waveFormat.nChannels = 2;
waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
waveFormat.cbSize = 0;
result = m_primaryBuffer->SetFormat(&waveFormat);
if (FAILED(result))
return false;
return true;
}
void SoundClass::ShutdownDirectSound()
{
if (m_primaryBuffer)
{
m_primaryBuffer->Release();
m_primaryBuffer = nullptr;
}
if (m_sound)
{
m_sound->Release();
m_sound = nullptr;
}
}
bool SoundClass::LoadWaveFile(char* filePath, IDirectSoundBuffer8** secondaryBuffer)
{
int error;
FILE* filePtr;
UINT count;
WaveHeaderType waveFileHeader;
WAVEFORMATEX waveFormat;
DSBUFFERDESC bufferDesc;
HRESULT result;
IDirectSoundBuffer* tempBuffer;
UCHAR* waveData;
UCHAR* bufferPtr;
ULONG bufferSize;
error = fopen_s(&filePtr, filePath, "rb");
if (error != 0)
return false;
count = (UINT)fread(&waveFileHeader, (size_t)sizeof(WaveHeaderType), 1, filePtr);
if (count != 1)
return false;
if (memcmp(waveFileHeader.chunkID, "RIFF", 4) || memcmp(waveFileHeader.format, "WAVE", 4) ||
memcmp(waveFileHeader.subChunkID, "fmt ", 4) || memcmp(waveFileHeader.dataChunkID, "data", 4))
return false;
if (waveFileHeader.audioFormat != WAVE_FORMAT_PCM)
return false;
if (waveFileHeader.numChannels != 2)
return false;
//if (waveFileHeader.sampleRate != 44100)
// return false;
if (waveFileHeader.bitsPerSample != 16)
return false;
::memset(&waveFormat, 0, sizeof(WAVEFORMATEX));
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nSamplesPerSec = 44100;
waveFormat.wBitsPerSample = 16;
waveFormat.nChannels = 2;
waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
waveFormat.cbSize = 0;
::memset(&bufferDesc, 0, sizeof(DSBUFFERDESC));
bufferDesc.dwSize = sizeof(DSBUFFERDESC);
bufferDesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS;
bufferDesc.dwBufferBytes = waveFileHeader.dataSize;
bufferDesc.dwReserved = 0;
bufferDesc.lpwfxFormat = &waveFormat;
bufferDesc.guid3DAlgorithm = GUID_NULL;
// Create a temporary sound buffer with the specific buffer settings
result = m_sound->CreateSoundBuffer(&bufferDesc, &tempBuffer, nullptr);
if (FAILED(result))
return false;
result = tempBuffer->QueryInterface(IID_IDirectSoundBuffer8, (void**)&*secondaryBuffer);
if (FAILED(result))
{
tempBuffer->Release();
tempBuffer = nullptr;
return false;
}
tempBuffer->Release();
tempBuffer = nullptr;
// Move th the begining of the wave data which starts at the end of the data chunk header
fseek(filePtr, sizeof(WaveHeaderType), SEEK_SET);
waveData = new UCHAR[waveFileHeader.dataSize];
if (!waveData)
return false;
count = (UINT)fread(waveData, 1, (size_t)waveFileHeader.dataSize, filePtr);
if (count != waveFileHeader.dataSize)
return false;
error = fclose(filePtr);
if (error != 0)
return false;
// Lock the secondary buufer to write wave data into it
result = (*secondaryBuffer)->Lock(0, waveFileHeader.dataSize, (void**)&bufferPtr, (LPDWORD)&bufferSize, nullptr, 0, 0);
if (FAILED(result))
return false;
::memcpy(bufferPtr, waveData, waveFileHeader.dataSize);
// Unlock the secondary buffer after the data has been written to it
result = (*secondaryBuffer)->Unlock((LPVOID)bufferPtr, bufferSize, nullptr, 0);
if (FAILED(result))
return false;
delete[] waveData;
waveData = nullptr;
return true;
}
void SoundClass::ShutdownWaveFile(IDirectSoundBuffer8** secondaryBuffer)
{
if (*secondaryBuffer)
{
(*secondaryBuffer)->Release();
*secondaryBuffer = nullptr;
}
}
bool SoundClass::PlayWaveFile(IDirectSoundBuffer8** secondaryBuffer)
{
HRESULT result;
result = (*secondaryBuffer)->SetCurrentPosition(0);
if (FAILED(result))
return false;
result = (*secondaryBuffer)->SetVolume(DSBVOLUME_MAX);
if (FAILED(result))
return false;
result = (*secondaryBuffer)->Play(0, 0, 0);
if (FAILED(result))
return false;
return true;
}
源码下载:下载地址