当使用 DirectSound 播放 PCM 音频时,可以通过回调函数来实现音频数据的传递和处理。以下是一个简单的示例代码,展示了如何使用回调函数来播放 PCM 音频:
#include <dsound.h>
// 定义音频缓冲区大小
const int BUFFER_SIZE = 4096;
// 音频缓冲区结构体
struct AudioBuffer {
BYTE* data; // 音频数据指针
DWORD dataSize; // 音频数据大小
};
// 回调函数,在音频缓冲区需要更多数据时被调用
DWORD WINAPI FillBuffer(LPVOID lpContext, LPVOID lpBufferData, DWORD dwBufferLength)
{
AudioBuffer* audioBuffer = static_cast<AudioBuffer*>(lpContext);
// 将音频数据拷贝到缓冲区中
memcpy(lpBufferData, audioBuffer->data, dwBufferLength);
// 更新音频数据指针和大小
audioBuffer->data += dwBufferLength;
audioBuffer->dataSize -= dwBufferLength;
if (audioBuffer->dataSize <= 0) {
// 如果音频数据已经全部播放完毕,返回 DS_OK 停止播放
return DS_OK;
} else {
// 返回 DSBSTATUS_CONTINUE 继续播放下一部分音频数据
return DSBSTATUS_CONTINUE;
}
}
int main()
{
IDirectSound8* pDS = nullptr; // DirectSound 对象指针
IDirectSoundBuffer* pDSBPrimary = nullptr; // 主要缓冲区指针
IDirectSoundBuffer* pDSBSecondary = nullptr; // 次要缓冲区指针
// 初始化 DirectSound 对象
DirectSoundCreate8(nullptr, &pDS, nullptr);
pDS->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
// 设置主要缓冲区参数
DSBUFFERDESC dsbdPrimary;
ZeroMemory(&dsbdPrimary, sizeof(DSBUFFERDESC));
dsbdPrimary.dwSize = sizeof(DSBUFFERDESC);
dsbdPrimary.dwFlags = DSBCAPS_PRIMARYBUFFER;
// 创建主要缓冲区
pDS->CreateSoundBuffer(&dsbdPrimary, &pDSBPrimary, nullptr);
// 设置次要缓冲区参数
WAVEFORMATEX waveFormat;
ZeroMemory(&waveFormat, sizeof(WAVEFORMATEX));
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nChannels = 2; // 双声道
waveFormat.nSamplesPerSec = 44100; // 采样率
waveFormat.nAvgBytesPerSec = 176400; // 平均每秒字节数 (采样率 * 位深度 * 声道数 / 8)
waveFormat.nBlockAlign = 4; // 每个采样的字节数 (位深度 * 声道数 / 8)
waveFormat.wBitsPerSample = 16; // 每个采样的位深度
// 计算音频数据总大小,这里假设音频数据已经加载到 audioBuffer 中
DWORD dataSize = ...;
// 创建次要缓冲区
DSBUFFERDESC dsbdSecondary;
ZeroMemory(&dsbdSecondary, sizeof(DSBUFFERDESC));
dsbdSecondary.dwSize = sizeof(DSBUFFERDESC);
dsbdSecondary.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
dsbdSecondary.dwBufferBytes = BUFFER_SIZE; // 缓冲区大小
dsbdSecondary.lpwfxFormat = &waveFormat; // 音频格式
pDS->CreateSoundBuffer(&dsbdSecondary, &pDSBSecondary, nullptr);
// 将音频数据传递给回调函数
AudioBuffer audioBuffer;
audioBuffer.data = ...; // 音频数据指针
audioBuffer.dataSize = dataSize; // 音频数据大小
pDSBSecondary->SetBufferData(audioBuffer.data, audioBuffer.dataSize);
// 设置回调函数
pDSBSecondary->SetNotificationPositions(1, &positionNotify);
// 开始播放音频缓冲区
pDSBPrimary->Play(0, 0, DSBPLAY_LOOPING);
pDSBSecondary->Play(0, 0, DSBPLAY_LOOPING);
while (true) {
// 处理其他逻辑,保持程序运行
}
// 释放资源和清理工作
pDSBPrimary->Stop();
pDSBPrimary->Release();
pDSBSecondary->Stop();
pDSBSecondary->Release();
pDS->Release();
return 0;
}
在这个示例代码中,我们创建了一个主要缓冲区和一个次要缓冲区。主要缓冲区用于控制整个 DirectSound 对象,而次要缓冲区用于实际播放 PCM 音频数据。
回调函数 FillBuffer 被设置为次要缓冲区的填充函数,在音频缓冲区需要更多数据时被调用。在回调函数中,我们将音频数据拷贝到缓冲区中,并更新音频数据指针和大小。
最后,我们通过 pDSBSecondary->Play() 方法来启动音频播放,并通过 while(true) 循环来保持程序运行。请注意,在实际开发中,还需要适当处理错误和释放资源的操作。<br/><br/><br/><p style="color:#CCCCCC;font-size:12px;">内容由零声教学AI助手提供,问题来源于学员提问</p> |