DirectSound学习笔记(7):缓冲区操作
填充和播放静态缓冲区
向一个静态缓冲区载入数据是包含三个步骤:
1. 使用IDirectSoundBuffer8::Lock将整个缓冲区锁定。你指定缓冲区中你打算开始写的偏移位置(通常为0),并返回该点的内存地址。
2. 使用标准的内存拷贝程序将音频数据写入返回的地址中。
3. 使用IDirectSoundBuffer8::Unlock为缓冲区解锁。
下面的例子显示了这些步骤,其中lpdsbStatic是一个IDirectSoundBuffer8接口指针,pbData是一个数据源地址:
DWORD dwLength;
if (DS_OK == lpdsbStatic->Lock(
0, // Offset at which to start lock.
0, // Size of lock; ignored because of flag.
&lpvWrite, // Gets address of first part of lock.
&dwLength, // Gets size of first part of lock.
NULL, // Address of wraparound not needed.
NULL, // Size of wraparound not needed.
DSBLOCK_ENTIREBUFFER)) // Flag.
{
memcpy(lpvWrite, pbData, dwLength);
lpdsbStatic->Unlock(
lpvWrite, // Address of lock start.
dwLength, // Size of lock.
NULL, // No wraparound portion.
0); // No wraparound size.
}
else
(
ErrorHandler(); // Add error-handling here.
}
调用IDirectSoundBuffer8::Play播放缓冲区,如下面的例子:
HRESULT hr = lpdsbStatic->Play(
0, // Unused.
0, // Priority for voice management.
0); // Flags.
if (FAILED(hr))
(
ErrorHandler(); // Add error-handling here.
}
因为这个例子中没有设置DSBPLAY_LOOPING标识,当缓冲区到达末尾时自动停止播放。你也可以使用IDirectSoundBuffer8::Stop过早停止它。当你过早停止一个缓冲区时,播放指针的位置仍然停留在那里。因此例子中调用IDirectSoundBuffer8::SetCurrentPosition,确保缓冲区从起始位置播放。
使用流缓冲区
一个流缓冲区播放一长段声音时,不能一次将所有声音装入缓冲区。当缓冲区播放时,旧数据周期性地被新数据替换。调用IDirectSoundBuffer8::Play方法播放流缓冲区,设置dsFlags参数为DSBPLAY_LOOPING。调用IDirectSoundBuffer8::Stop方法停止播放。这个方法立即停止缓冲区,因此你需要确保所有数据已经被播放。这个可以通过轮流检查播放位置或设置通知位置来实现。
流入一个缓冲区需要以下步骤:
1. 确定缓冲区是否已做好接收新数据的准备。这个可以通过轮流检查播放指针或等待通知来实现。
2. 使用IDirectSoundBuffer8::Lock锁定缓冲区的一部分。这个方法返回一个或两个地址,从该地址数据能够被立刻写入。
3. 使用标准内存拷贝程序将音频数据写入返回的地址中。
4. 使用IDirectSoundBuffer8::Unlock为缓冲区解锁。
IDirectSoundBuffer8::Lock可能返回两个地址的原因是你能够锁定多达缓冲区大小的任意字节数,而忽略起始点。例如,假设你在一个40 000字节缓冲区的20 000字节处开始锁定30 000字节。这时你必须进行两次独立内存拷贝。
虽然可以锁定整个缓冲区,但是在缓冲区播放时你不能这么做。通常你每次只更新缓冲区的一小部分。例如,在播放指针到达缓冲区的四分之二时,你能够锁定缓冲区的四分之一部分并写入数据。你永远不能对位于播放指针和写指针之间的缓冲区部分进行写操作。
下面的函数向一个声音缓冲区写数据,起始位置由dwOffset传入:
LPDIRECTSOUNDBUFFER8 lpDsb, // The buffer.
DWORD dwOffset, // Our own write cursor.
LPBYTE lpbSoundData, // Start of our data.
DWORD dwSoundBytes) // Size of block to copy.
{
LPVOID lpvPtr1;
DWORD dwBytes1;
LPVOID lpvPtr2;
DWORD dwBytes2;
HRESULT hr;
// Obtain memory address of write block. This will be in two parts
// if the block wraps around.
hr = lpDsb->Lock(dwOffset, dwSoundBytes, &lpvPtr1,
&dwBytes1, &lpvPtr2, &dwBytes2, 0);
// If the buffer was lost, restore and retry lock.
if (DSERR_BUFFERLOST == hr)
{
lpDsb->Restore();
hr = lpDsb->Lock(dwOffset, dwSoundBytes,
&lpvPtr1, &dwBytes1,
&lpvPtr2, &dwBytes2, 0);
}
if (SUCCEEDED(hr))
{
// Write to pointers.
CopyMemory(lpvPtr1, lpbSoundData, dwBytes1);
if (NULL != lpvPtr2)
{
CopyMemory(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2);
}
// Release the data back to DirectSound.
hr = lpDsb->Unlock(lpvPtr1, dwBytes1, lpvPtr2,
dwBytes2);
if (SUCCEEDED(hr))
{
// Success.
return TRUE;
}
}
// Lock, Unlock, or Restore failed.
return FALSE;
}