用DirectX Audio和DirectShow播放声音和音乐(2)
2010-05-17 23:53
323 查看
本篇是用
DirectX
Audio和DirectShow播放声音和音乐(1)
的续篇。
开始使用主音频缓存
让缓存在程序启动的时候开始播放可以节省不少处理器时间。因为内存资源是有限的,特别是在硬件设备中,而你使用的数据缓存可能需要任意大小,因此主音频缓
冲区和辅助缓冲区使用环形缓存。
环形缓冲的示意图如下:
因为数据缓冲是一个一维数组,所以可以让这个缓冲区头尾相接。这是一个十分强大的技术,利用这个技术我们可以节省大量的内存。
声音在进行混音处理后,被送入环形主音频缓存。一旦播放位置到达主音频缓存的终点,声音又从头开始播放,这样声音就被无间隙地连续播放。如果想要使用缓存
的这种循环特性,需要指定启用循环播放的特性,若不然当播放到缓冲区终点时,播放就停止了。
为了播放缓存中的音频数据(在开启循环选项的情况下播放),需要调用Play函数。
The Play method causes the
sound
buffer to play, starting at the play cursor.
Parameters
dwReserved1
Reserved. Must be
0.
dwPriority
Priority for the
sound,
used by the voice manager when assigning hardware mixing resources. The
lowest priority is 0, and the highest priority is 0xFFFFFFFF. If the
buffer
was not created with the DSBCAPS_LOCDEFER flag, this value must be 0.
dwFlags
Flags specifying
how to
play the buffer. The following flags are defined:
Looping flag
Voice allocation flags
The voice
allocation flags
are valid only for buffers created with the DSBCAPS_LOCDEFER flag. One
of
the following flags can be used to force the processing of the sound
into
hardware or software. If neither DBSPLAY_LOCHARDWARE nor
DBSPLAY_LOCSOFTWARE
is set, the sound is played in either software or hardware, depending on
the
availability of resources at the time the method is called. See Remarks.
Voice management flags
The voice
management flags
are valid only for buffers created with the DSBCAPS_LOCDEFER flag, and
are
used for sounds that are to play in hardware. These flags enable
hardware
resources that are already in use to be yielded to the current sound.
Only
buffers created with the DSBCAPS_LOCDEFER flag are candidates for
premature
termination. See Remarks.
Return Values
If the method succeeds,
the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
当完成主音频缓存设置后(以及整个音响系统),如果想停止它,需要调用IDirectSoundBuffer::Stop。
The Stop method causes
the sound
buffer to stop playing.
Parameters
None.
Return Values
If the method succeeds,
the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
Remarks
For secondary sound
buffers,
IDirectSoundBuffer8::Stop sets the play cursor to the sample that
follows the
last sample played. This means that when the Play method is next called
on the
buffer, it will continue playing where it left off.
For the primary buffer,
if an
application has the DSSCL_WRITEPRIMARY level, this method will stop the
buffer
and reset the play cursor to 0 (the beginning of the buffer). This is
necessary
because the primary buffers on most sound cards can play only from the
beginning
of the buffer.
However, if
IDirectSoundBuffer8::Stop is called on a primary buffer and the
application has
a cooperative level other than DSSCL_WRITEPRIMARY, this method simply
reverses
the effects of IDirectSoundBuffer8::Play. It configures the primary
buffer to
stop if no secondary buffers are playing. If other buffers are playing
in this
or other applications, the primary buffer will not actually stop until
they are
stopped. This method is useful because playing the primary buffer
consumes
processing overhead even if the buffer is playing sound data with the
amplitude
of 0 decibels.
使用辅助音频缓存
想要使用辅助音频缓存的数据,需要将辅助音频缓存中包含的数据填充到主音频缓存,如下图所示:
如果在主音频缓存的同一区域写入两段音频数据,这两段声音就会被同时播放。辅助音频缓存使用
IDirectSoundBuffer8接口,这个接口和IDirectSoundBuffer十分类似,实际上如果想要创建
IDirectSoundBuffer8接口,必须先创建IDirectSoundBuffer接口,然后通过请求新接口来获取
IDirectSoundBuffer8接口。
创建辅助音频缓存和创建主音频缓存时主要的不同是必须在初始化时设置回放格式,这意味着缓冲区只能使用一种格式。如果希望使用另外的格式,必须释放当前的
缓存,重新创建另外一个。
代码示例:
// initialize and configure directsound
// creates and initializes an object that supports the IDirectSound8 interface
if
(FAILED(DirectSoundCreate8(NULL, &g_ds, NULL)))
{
MessageBox(NULL, "Unable to create DirectSound object", "Error", MB_OK);
return
0;
}
// set the cooperative level of the application for this sound device
g_ds->SetCooperativeLevel(g_hwnd, DSSCL_NORMAL);
// create a sound buffer
// setup the W***EFORMATEX structure
W***EFORMATEX wave_format;
ZeroMemory(&wave_format,
sizeof
(W***EFORMATEX));
wave_format.wFormatTag = W***E_FORMAT_PCM;
wave_format.nChannels = 1;
// mono
wave_format.nSamplesPerSec = 11025;
wave_format.wBitsPerSample = 16;
wave_format.nBlockAlign = (wave_format.wBitsPerSample / 8) * wave_format.nChannels;
wave_format.nAvgBytesPerSec = wave_format.nSamplesPerSec * wave_format.nBlockAlign;
// setup the DSBUFFERDESC structure
DSBUFFERDESC ds_buffer_desc;
// zero out strcutre
ZeroMemory(&ds_buffer_desc,
sizeof
(DSBUFFERDESC));
ds_buffer_desc.dwSize =
sizeof
(DSBUFFERDESC);
ds_buffer_desc.dwFlags = DSBCAPS_CTRLVOLUME;
ds_buffer_desc.dwBufferBytes = wave_format.nAvgBytesPerSec * 2;
// 2 seconds
ds_buffer_desc.lpwfxFormat = &wave_format;
// create the fist version object
if
(FAILED(g_ds->CreateSoundBuffer(&ds_buffer_desc, &ds, NULL)))
{
// error ocuurred
MessageBox(NULL, "Unable to create sound buffer", "Error", MB_OK);
}
else
{
// get the version 8 interface
ds->QueryInterface(IID_IDirectSoundBuffer8, (
void
**)&g_ds_buffer);
// release the original interface
ds->Release();
}
锁定和加载 ---
在缓冲区中加载数据
缓存接口有一对处理数据加载的函数。 IDirectSoundBuffer8::Lock函数锁定音频缓冲数据,并且找回指向缓冲区数据的数据指针;
IDirectSoundBuffer8::Unlock释放在锁定操作中使用的资源。
当锁定缓存以后,就能对缓冲区进行写入操作。先告诉缓冲写入偏移量的字节数、写入位置、要写入数据的字节数,写入完成后返回两个指向数据的指针以及写入数
据的字节数。为什么会有两个指针和两个数据大小呢?因为音频数据缓冲区是环形的,写入的数据也需要跨过起始位置。返回的第一个指针是请求的位置,第一个值
是从请求的位置到结束所写入的字节数,第二个指针通常是缓存区的起始位置,第二个值是从起点开始写入的字节数。
这里有一个长度是
65536字节大小的缓冲区,这个缓冲区被锁定,以便于写入62000字节的数据。第一个指针处写入了60000字节,第二个指针处还剩下2000字节,
如下图所示:
Lock函数使用信息如下:
The Lock method readies
all or
part of the buffer for a data write and returns pointers to which data
can be
written.
Parameters
dwOffset
Offset, in bytes,
from the
start of the buffer to the point where the lock begins. This parameter
is
ignored if DSBLOCK_FROMWRITECURSOR is specified in the dwFlags
parameter.
dwBytes
Size, in bytes, of
the
portion of the buffer to lock. The buffer is conceptually circular, so
this
number can exceed the number of bytes between dwOffset and the end of
the
buffer.
ppvAudioPtr1
Address of a
variable that
receives a pointer to the first locked part of the buffer.
pdwAudioBytes1
Address of a
variable that
receives the number of bytes in the block at ppvAudioPtr1. If this value
is
less than dwBytes, the lock has wrapped and ppvAudioPtr2 points to a
second
block of data at the beginning of the buffer .
ppvAudioPtr2
Address of a
variable that
receives a pointer to the second locked part of the capture buffer. If
NULL
is returned, the ppvAudioPtr1 parameter points to the entire locked
portion
of the capture buffer.
pdwAudioBytes2
Address of a
variable that
receives the number of bytes in the block at ppvAudioPtr2. If
ppvAudioPtr2
is NULL, this value is zero.
dwFlags
Flags modifying
the lock
event. The following flags are defined:
Return Values
If the method succeeds,
the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
Remarks
This method accepts an
offset
and a byte count, and returns two write pointers and their associated
sizes. If
the locked portion does not extend to the end of the buffer and wrap to
the
beginning, the second pointer, ppvAudioBytes2, receives NULL. If the
lock does
wrap, ppvAudioBytes2 points to the beginning of the buffer.
If the application
passes NULL
for the ppvAudioPtr2 and pdwAudioBytes2 parameters, the lock extends no
further
than the end of the buffer and does not wrap.
After writing data to
the
pointers returned by this method, the application must immediately call
Unlock
to notify DirectSound that the data is ready for playback. Failure to do
so can
cause audio breakup or silence on some sound device configurations.
This method returns
write
pointers only. The application should not try to read sound data from
this
pointer, because the data might not be valid. For example, if the buffer
is
located in on-card memory, the pointer might be an address to a
temporary buffer
in system memory. When IDirectSoundBuffer8::Unlock is called, the
contents of
this temporary buffer are transferred to the on-card memory.
以下这段代码锁定辅助音频缓冲区,并且填充随机数据。
// lock buffer, fill with random values, and unlock.
char
* ptr;
DWORD size;
// readies all or part of the buffer for a data write and returns pointers to which data can be written
if
(SUCCEEDED(g_ds_buffer->Lock(0, 0, (
void
**)&ptr, &size, NULL, 0, DSBLOCK_ENTIREBUFFER)))
{
for
(DWORD i = 0; i < size; i++)
ptr[i] = rand() % 256;
}
填充完数据后,应该给缓冲区解锁,调用IDirectSoundBuffer8::Unlock解锁。
The Unlock method
releases a
locked sound buffer.
Parameters
pvAudioPtr1
Address of the
value
retrieved in the ppvAudioPtr1 parameter of the Lock method.
dwAudioBytes1
Number of
bytes written to
the portion of the buffer at pvAudioPtr1. See Remarks.
pvAudioPtr2
Address of the
value
retrieved in the ppvAudioPtr2 parameter of the IDirectSoundBuffer8::Lock
method.
dwAudioBytes2
Number of
bytes written to
the portion of the buffer at pvAudioPtr2. See Remarks.
Return Values
If the method succeeds,
the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
Remarks
An application must pass
both
pointers, pvAudioPtr1 and pvAudioPtr2, returned by the
IDirectSoundBuffer8::Lock
method to ensure the correct pairing of IDirectSoundBuffer8::Lock and
IDirectSoundBuffer8::Unlock. The second pointer is needed even if
nothing was
written to the second pointer.
The values in
dwAudioBytes1 and
dwAudioBytes2 must specify the number of bytes actually written to each
part of
the buffer, which might be less than the size of the lock. DirectSound
uses
these values to determine how much data to commit to the device.
以下代码解锁刚才锁定的辅助音频缓冲区:
// releases a locked sound buffer
g_ds_buffer->Unlock(ptr, size, NULL, 0);
播放缓冲区中的声音
在辅助音频缓冲区中播放音频和在主音频缓冲区中播放是一样的,惟一不同的是这次需要设置播放的起始位置,通过IDirectSoundBuffer8::
SetCurrentPosition来设置。一般情况下,会选择从缓冲的开头播放,需要注意的是,停止播放一段音频并不会重置播放位置,所以可以通过停
止播放来达到暂停的目的,只需要再次调用播放函数来恢复播放。
The SetCurrentPosition
method
sets the position of the play cursor, which is the point at which the
next byte
of data is read from the buffer.
Parameters
dwNewPosition
Offset of
the play cursor,
in bytes, from the beginning of the buffer.
Return Values
If the method succeeds,
the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
Remarks
This method cannot be
called on
the primary buffer.
If the buffer is
playing, the
cursor immediately moves to the new position and play continues from
that point.
If it is not playing, playback will begin from the new position the next
time
the Play method is called.
改变音量
DirectSound
在播放声音时按照采样时的最大音量播放,加之受限于音频硬件,所以没有办法让音量变得更大,DirectSound只能让音量变得更小些。这是通过减小声
音级别来完成的,这个级别的单位是百分之一分贝。范围从0(最大音量)--
10000(无声),利用这个特性可以在程序中加入“淡入”或“淡出“的效果。
要调整音量,可以使用 IDirectSoundBuffer8::SetVolume函数。
The SetVolume method
sets the
attenuation of the sound.
Parameters
lVolume
Attenuation, in
hundredths
of a decibel (dB).
Return Values
If the method succeeds,
the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
Remarks
Allowable values are
between
DSBVOLUME_MAX (no attenuation) and DSBVOLUME_MIN (silence). These values
are
defined in Dsound.h as 0 and ?10,000 respectively. The value
DSBVOLUME_MAX
represents the original, unadjusted volume of the stream. The value
DSBVOLUME_MIN indicates an audio volume attenuated by 100 dB, which, for
all
practical purposes, is silence. DirectSound does not support
amplification.
以下给出一个完整示例来运用刚才学到的知识。
点击下载源码和工程
源码示例:
/***************************************************************************************
PURPOSE:
Lock Load Playing Demo
***************************************************************************************/
#define
DIRECTINPUT_VERSION 0x0800
#include <windows.h>
#include <stdio.h>
#include <dsound.h>
#include "resource.h"
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dsound.lib")
#pragma warning(disable : 4996)
#define
Safe_Release(p) if((p)) (p)->Release();
// window handles, class and caption text.
HWND g_hwnd;
char
g_class_name[] = "LockLoadClass";
IDirectSound8* g_ds;
// directsound component
IDirectSoundBuffer8* g_ds_buffer;
// sound buffer object
//--------------------------------------------------------------------------------
// Window procedure.
//--------------------------------------------------------------------------------
long
WINAPI Window_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch
(msg)
{
case
WM_DESTROY:
PostQuitMessage(0);
return
0;
}
return
(
long
) DefWindowProc(hwnd, msg, wParam, lParam);
}
//--------------------------------------------------------------------------------
// Main function, routine entry.
//--------------------------------------------------------------------------------
int
WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line,
int
cmd_show)
{
WNDCLASS win_class;
MSG msg;
IDirectSoundBuffer* ds = NULL;
// create window class and register it
win_class.style = CS_HREDRAW | CS_VREDRAW;
win_class.lpfnWndProc = Window_Proc;
win_class.cbClsExtra = 0;
win_class.cbWndExtra = DLGWINDOWEXTRA;
win_class.hInstance = inst;
win_class.hIcon = LoadIcon(inst, IDI_APPLICATION);
win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
win_class.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
win_class.lpszMenuName = NULL;
win_class.lpszClassName = g_class_name;
if
(! RegisterClass(&win_class))
return
FALSE;
// create the main window
g_hwnd = CreateDialog(inst, MAKEINTRESOURCE(IDD_LOCKLOAD), 0, NULL);
ShowWindow(g_hwnd, cmd_show);
UpdateWindow(g_hwnd);
// initialize and configure directsound
// creates and initializes an object that supports the IDirectSound8 interface
if
(FAILED(DirectSoundCreate8(NULL, &g_ds, NULL)))
{
MessageBox(NULL, "Unable to create DirectSound object", "Error", MB_OK);
return
0;
}
// set the cooperative level of the application for this sound device
g_ds->SetCooperativeLevel(g_hwnd, DSSCL_NORMAL);
// create a sound buffer
// setup the W***EFORMATEX structure
W***EFORMATEX wave_format;
ZeroMemory(&wave_format,
sizeof
(W***EFORMATEX));
wave_format.wFormatTag = W***E_FORMAT_PCM;
wave_format.nChannels = 1;
// mono
wave_format.nSamplesPerSec = 11025;
wave_format.wBitsPerSample = 16;
wave_format.nBlockAlign = (wave_format.wBitsPerSample / 8) * wave_format.nChannels;
wave_format.nAvgBytesPerSec = wave_format.nSamplesPerSec * wave_format.nBlockAlign;
// setup the DSBUFFERDESC structure
DSBUFFERDESC ds_buffer_desc;
// zero out strcutre
ZeroMemory(&ds_buffer_desc,
sizeof
(DSBUFFERDESC));
ds_buffer_desc.dwSize =
sizeof
(DSBUFFERDESC);
ds_buffer_desc.dwFlags = DSBCAPS_CTRLVOLUME;
ds_buffer_desc.dwBufferBytes = wave_format.nAvgBytesPerSec * 2;
// 2 seconds
ds_buffer_desc.lpwfxFormat = &wave_format;
// create the fist version object
if
(FAILED(g_ds->CreateSoundBuffer(&ds_buffer_desc, &ds, NULL)))
{
// error ocuurred
MessageBox(NULL, "Unable to create sound buffer", "Error", MB_OK);
}
else
{
// get the version 8 interface
ds->QueryInterface(IID_IDirectSoundBuffer8, (
void
**)&g_ds_buffer);
// release the original interface
ds->Release();
// lock buffer, fill with random values, and unlock.
char
* ptr;
DWORD size;
// readies all or part of the buffer for a data write and returns pointers to which data can be written
if
(SUCCEEDED(g_ds_buffer->Lock(0, 0, (
void
**)&ptr, &size, NULL, 0, DSBLOCK_ENTIREBUFFER)))
{
for
(DWORD i = 0; i < size; i++)
ptr[i] = rand() % 256;
}
// releases a locked sound buffer
g_ds_buffer->Unlock(ptr, size, NULL, 0);
// play sound looping
// sets the position of the play cursor,
// which is the point at which the next byte of data is read from the buffer.
g_ds_buffer->SetCurrentPosition(0);
// set the attenuation of the sound
g_ds_buffer->SetVolume(DSBVOLUME_MAX);
// causes the sound buffer to play, starting at the play cursor.
g_ds_buffer->Play(0, 0, DSBPLAY_LOOPING);
}
// start message pump, waiting for signal to quit.
ZeroMemory(&msg,
sizeof
(MSG));
while
(msg.message != WM_QUIT)
{
if
(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// release directsound objects
g_ds->Release();
UnregisterClass(g_class_name, inst);
return
(
int
) msg.wParam;
}
运行截图:
阅读下篇:用
DirectX
Audio和DirectShow播放声音和音乐(3)
DirectX
Audio和DirectShow播放声音和音乐(1)
的续篇。
开始使用主音频缓存
让缓存在程序启动的时候开始播放可以节省不少处理器时间。因为内存资源是有限的,特别是在硬件设备中,而你使用的数据缓存可能需要任意大小,因此主音频缓
冲区和辅助缓冲区使用环形缓存。
环形缓冲的示意图如下:
因为数据缓冲是一个一维数组,所以可以让这个缓冲区头尾相接。这是一个十分强大的技术,利用这个技术我们可以节省大量的内存。
声音在进行混音处理后,被送入环形主音频缓存。一旦播放位置到达主音频缓存的终点,声音又从头开始播放,这样声音就被无间隙地连续播放。如果想要使用缓存
的这种循环特性,需要指定启用循环播放的特性,若不然当播放到缓冲区终点时,播放就停止了。
为了播放缓存中的音频数据(在开启循环选项的情况下播放),需要调用Play函数。
The Play method causes the
sound
buffer to play, starting at the play cursor.
HRESULT Play( DWORD dwReserved1 , DWORD dwPriority , DWORD dwFlags );
Parameters
dwReserved1Reserved. Must be
0.
dwPriority
Priority for the
sound,
used by the voice manager when assigning hardware mixing resources. The
lowest priority is 0, and the highest priority is 0xFFFFFFFF. If the
buffer
was not created with the DSBCAPS_LOCDEFER flag, this value must be 0.
dwFlags
Flags specifying
how to
play the buffer. The following flags are defined:
Looping flag
Value | Description |
DSBPLAY_LOOPING | After the end of the audio buffer is reached, play restarts at the beginning of the buffer. Play continues until explicitly stopped. This flag must be set when playing a primary buffer. |
The voice
allocation flags
are valid only for buffers created with the DSBCAPS_LOCDEFER flag. One
of
the following flags can be used to force the processing of the sound
into
hardware or software. If neither DBSPLAY_LOCHARDWARE nor
DBSPLAY_LOCSOFTWARE
is set, the sound is played in either software or hardware, depending on
the
availability of resources at the time the method is called. See Remarks.
Value | Description |
DSBPLAY_LOCHARDWARE | Play this voice in a hardware buffer only. If the hardware has no available voices and no voice management flags are set, the call to IDirectSoundBuffer8::Play fails. This flag cannot be combined with DSBPLAY_LOCSOFTWARE. |
DSBPLAY_LOCSOFTWARE | Play this voice in a software buffer only. This flag cannot be combined with DSBPLAY_LOCHARDWARE or any voice management flag. |
The voice
management flags
are valid only for buffers created with the DSBCAPS_LOCDEFER flag, and
are
used for sounds that are to play in hardware. These flags enable
hardware
resources that are already in use to be yielded to the current sound.
Only
buffers created with the DSBCAPS_LOCDEFER flag are candidates for
premature
termination. See Remarks.
Value | Description |
DSBPLAY_TERMINATEBY_TIME | If the hardware has no available voices, a currently playing nonlooping buffer will be stopped to make room for the new buffer. The buffer prematurely terminated is the one with the least time left to play. |
DSBPLAY_TERMINATEBY_DISTANCE | If the hardware has no available voices, a currently playing buffer will be stopped to make room for the new buffer. The buffer prematurely terminated will be selected from buffers that have the buffer's DSBCAPS_ MUTE3DATMAXDISTANCE flag set and are beyond their maximum distance. If there are no such buffers, the method fails. |
DSBPLAY_TERMINATEBY_PRIORITY | If the hardware has no available voices, a currently playing buffer will be stopped to make room for the new buffer. The buffer prematurely terminated will be the one with the lowest priority as set by the dwPriority parameter passed to IDirectSoundBuffer8::Play for the buffer. |
Return Values
If the method succeeds, the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
Return code |
DSERR_BUFFERLOST |
DSERR_INVALIDCALL |
DSERR_INVALIDPARAM |
DSERR_PRIOLEVELNEEDED |
The Stop method causes
the sound
buffer to stop playing.
HRESULT Stop();
Parameters
None.Return Values
If the method succeeds, the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
Remarks
For secondary sound buffers,
IDirectSoundBuffer8::Stop sets the play cursor to the sample that
follows the
last sample played. This means that when the Play method is next called
on the
buffer, it will continue playing where it left off.
For the primary buffer,
if an
application has the DSSCL_WRITEPRIMARY level, this method will stop the
buffer
and reset the play cursor to 0 (the beginning of the buffer). This is
necessary
because the primary buffers on most sound cards can play only from the
beginning
of the buffer.
However, if
IDirectSoundBuffer8::Stop is called on a primary buffer and the
application has
a cooperative level other than DSSCL_WRITEPRIMARY, this method simply
reverses
the effects of IDirectSoundBuffer8::Play. It configures the primary
buffer to
stop if no secondary buffers are playing. If other buffers are playing
in this
or other applications, the primary buffer will not actually stop until
they are
stopped. This method is useful because playing the primary buffer
consumes
processing overhead even if the buffer is playing sound data with the
amplitude
of 0 decibels.
使用辅助音频缓存
想要使用辅助音频缓存的数据,需要将辅助音频缓存中包含的数据填充到主音频缓存,如下图所示:
如果在主音频缓存的同一区域写入两段音频数据,这两段声音就会被同时播放。辅助音频缓存使用
IDirectSoundBuffer8接口,这个接口和IDirectSoundBuffer十分类似,实际上如果想要创建
IDirectSoundBuffer8接口,必须先创建IDirectSoundBuffer接口,然后通过请求新接口来获取
IDirectSoundBuffer8接口。
创建辅助音频缓存和创建主音频缓存时主要的不同是必须在初始化时设置回放格式,这意味着缓冲区只能使用一种格式。如果希望使用另外的格式,必须释放当前的
缓存,重新创建另外一个。
代码示例:
// initialize and configure directsound
// creates and initializes an object that supports the IDirectSound8 interface
if
(FAILED(DirectSoundCreate8(NULL, &g_ds, NULL)))
{
MessageBox(NULL, "Unable to create DirectSound object", "Error", MB_OK);
return
0;
}
// set the cooperative level of the application for this sound device
g_ds->SetCooperativeLevel(g_hwnd, DSSCL_NORMAL);
// create a sound buffer
// setup the W***EFORMATEX structure
W***EFORMATEX wave_format;
ZeroMemory(&wave_format,
sizeof
(W***EFORMATEX));
wave_format.wFormatTag = W***E_FORMAT_PCM;
wave_format.nChannels = 1;
// mono
wave_format.nSamplesPerSec = 11025;
wave_format.wBitsPerSample = 16;
wave_format.nBlockAlign = (wave_format.wBitsPerSample / 8) * wave_format.nChannels;
wave_format.nAvgBytesPerSec = wave_format.nSamplesPerSec * wave_format.nBlockAlign;
// setup the DSBUFFERDESC structure
DSBUFFERDESC ds_buffer_desc;
// zero out strcutre
ZeroMemory(&ds_buffer_desc,
sizeof
(DSBUFFERDESC));
ds_buffer_desc.dwSize =
sizeof
(DSBUFFERDESC);
ds_buffer_desc.dwFlags = DSBCAPS_CTRLVOLUME;
ds_buffer_desc.dwBufferBytes = wave_format.nAvgBytesPerSec * 2;
// 2 seconds
ds_buffer_desc.lpwfxFormat = &wave_format;
// create the fist version object
if
(FAILED(g_ds->CreateSoundBuffer(&ds_buffer_desc, &ds, NULL)))
{
// error ocuurred
MessageBox(NULL, "Unable to create sound buffer", "Error", MB_OK);
}
else
{
// get the version 8 interface
ds->QueryInterface(IID_IDirectSoundBuffer8, (
void
**)&g_ds_buffer);
// release the original interface
ds->Release();
}
锁定和加载 ---
在缓冲区中加载数据
缓存接口有一对处理数据加载的函数。 IDirectSoundBuffer8::Lock函数锁定音频缓冲数据,并且找回指向缓冲区数据的数据指针;
IDirectSoundBuffer8::Unlock释放在锁定操作中使用的资源。
当锁定缓存以后,就能对缓冲区进行写入操作。先告诉缓冲写入偏移量的字节数、写入位置、要写入数据的字节数,写入完成后返回两个指向数据的指针以及写入数
据的字节数。为什么会有两个指针和两个数据大小呢?因为音频数据缓冲区是环形的,写入的数据也需要跨过起始位置。返回的第一个指针是请求的位置,第一个值
是从请求的位置到结束所写入的字节数,第二个指针通常是缓存区的起始位置,第二个值是从起点开始写入的字节数。
这里有一个长度是
65536字节大小的缓冲区,这个缓冲区被锁定,以便于写入62000字节的数据。第一个指针处写入了60000字节,第二个指针处还剩下2000字节,
如下图所示:
Lock函数使用信息如下:
The Lock method readies
all or
part of the buffer for a data write and returns pointers to which data
can be
written.
HRESULT Lock( DWORD dwOffset , DWORD dwBytes , LPVOID * ppvAudioPtr1 , LPDWORD pdwAudioBytes1 , LPVOID * ppvAudioPtr2 , LPDWORD pdwAudioBytes2 , DWORD dwFlags );
Parameters
dwOffsetOffset, in bytes,
from the
start of the buffer to the point where the lock begins. This parameter
is
ignored if DSBLOCK_FROMWRITECURSOR is specified in the dwFlags
parameter.
dwBytes
Size, in bytes, of
the
portion of the buffer to lock. The buffer is conceptually circular, so
this
number can exceed the number of bytes between dwOffset and the end of
the
buffer.
ppvAudioPtr1
Address of a
variable that
receives a pointer to the first locked part of the buffer.
pdwAudioBytes1
Address of a
variable that
receives the number of bytes in the block at ppvAudioPtr1. If this value
is
less than dwBytes, the lock has wrapped and ppvAudioPtr2 points to a
second
block of data at the beginning of the buffer .
ppvAudioPtr2
Address of a
variable that
receives a pointer to the second locked part of the capture buffer. If
NULL
is returned, the ppvAudioPtr1 parameter points to the entire locked
portion
of the capture buffer.
pdwAudioBytes2
Address of a
variable that
receives the number of bytes in the block at ppvAudioPtr2. If
ppvAudioPtr2
is NULL, this value is zero.
dwFlags
Flags modifying
the lock
event. The following flags are defined:
Value | Description |
DSBLOCK_FROMWRITECURSOR | Start the lock at the write cursor. The dwOffset parameter is ignored. |
DSBLOCK_ENTIREBUFFER | Lock the entire buffer. The dwBytes parameter is ignored. |
Return Values
If the method succeeds, the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
Return code |
DSERR_BUFFERLOST |
DSERR_INVALIDCALL |
DSERR_INVALIDPARAM |
DSERR_PRIOLEVELNEEDED |
Remarks
This method accepts an offset
and a byte count, and returns two write pointers and their associated
sizes. If
the locked portion does not extend to the end of the buffer and wrap to
the
beginning, the second pointer, ppvAudioBytes2, receives NULL. If the
lock does
wrap, ppvAudioBytes2 points to the beginning of the buffer.
If the application
passes NULL
for the ppvAudioPtr2 and pdwAudioBytes2 parameters, the lock extends no
further
than the end of the buffer and does not wrap.
After writing data to
the
pointers returned by this method, the application must immediately call
Unlock
to notify DirectSound that the data is ready for playback. Failure to do
so can
cause audio breakup or silence on some sound device configurations.
This method returns
write
pointers only. The application should not try to read sound data from
this
pointer, because the data might not be valid. For example, if the buffer
is
located in on-card memory, the pointer might be an address to a
temporary buffer
in system memory. When IDirectSoundBuffer8::Unlock is called, the
contents of
this temporary buffer are transferred to the on-card memory.
以下这段代码锁定辅助音频缓冲区,并且填充随机数据。
// lock buffer, fill with random values, and unlock.
char
* ptr;
DWORD size;
// readies all or part of the buffer for a data write and returns pointers to which data can be written
if
(SUCCEEDED(g_ds_buffer->Lock(0, 0, (
void
**)&ptr, &size, NULL, 0, DSBLOCK_ENTIREBUFFER)))
{
for
(DWORD i = 0; i < size; i++)
ptr[i] = rand() % 256;
}
填充完数据后,应该给缓冲区解锁,调用IDirectSoundBuffer8::Unlock解锁。
The Unlock method
releases a
locked sound buffer.
HRESULT Unlock( LPVOID pvAudioPtr1 , DWORD dwAudioBytes1 , LPVOID pvAudioPtr2 , DWORD dwAudioBytes2 );
Parameters
pvAudioPtr1Address of the
value
retrieved in the ppvAudioPtr1 parameter of the Lock method.
dwAudioBytes1
Number of
bytes written to
the portion of the buffer at pvAudioPtr1. See Remarks.
pvAudioPtr2
Address of the
value
retrieved in the ppvAudioPtr2 parameter of the IDirectSoundBuffer8::Lock
method.
dwAudioBytes2
Number of
bytes written to
the portion of the buffer at pvAudioPtr2. See Remarks.
Return Values
If the method succeeds, the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
Return code |
DSERR_INVALIDCALL |
DSERR_INVALIDPARAM |
DSERR_PRIOLEVELNEEDED |
Remarks
An application must passboth
pointers, pvAudioPtr1 and pvAudioPtr2, returned by the
IDirectSoundBuffer8::Lock
method to ensure the correct pairing of IDirectSoundBuffer8::Lock and
IDirectSoundBuffer8::Unlock. The second pointer is needed even if
nothing was
written to the second pointer.
The values in
dwAudioBytes1 and
dwAudioBytes2 must specify the number of bytes actually written to each
part of
the buffer, which might be less than the size of the lock. DirectSound
uses
these values to determine how much data to commit to the device.
以下代码解锁刚才锁定的辅助音频缓冲区:
// releases a locked sound buffer
g_ds_buffer->Unlock(ptr, size, NULL, 0);
播放缓冲区中的声音
在辅助音频缓冲区中播放音频和在主音频缓冲区中播放是一样的,惟一不同的是这次需要设置播放的起始位置,通过IDirectSoundBuffer8::
SetCurrentPosition来设置。一般情况下,会选择从缓冲的开头播放,需要注意的是,停止播放一段音频并不会重置播放位置,所以可以通过停
止播放来达到暂停的目的,只需要再次调用播放函数来恢复播放。
The SetCurrentPosition
method
sets the position of the play cursor, which is the point at which the
next byte
of data is read from the buffer.
HRESULT SetCurrentPosition( DWORD dwNewPosition );
Parameters
dwNewPositionOffset of
the play cursor,
in bytes, from the beginning of the buffer.
Return Values
If the method succeeds, the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
Return code |
DSERR_INVALIDCALL |
DSERR_INVALIDPARAM |
DSERR_PRIOLEVELNEEDED |
Remarks
This method cannot be called on
the primary buffer.
If the buffer is
playing, the
cursor immediately moves to the new position and play continues from
that point.
If it is not playing, playback will begin from the new position the next
time
the Play method is called.
改变音量
DirectSound
在播放声音时按照采样时的最大音量播放,加之受限于音频硬件,所以没有办法让音量变得更大,DirectSound只能让音量变得更小些。这是通过减小声
音级别来完成的,这个级别的单位是百分之一分贝。范围从0(最大音量)--
10000(无声),利用这个特性可以在程序中加入“淡入”或“淡出“的效果。
要调整音量,可以使用 IDirectSoundBuffer8::SetVolume函数。
The SetVolume method
sets the
attenuation of the sound.
HRESULT SetVolume( LONG lVolume );
Parameters
lVolumeAttenuation, in
hundredths
of a decibel (dB).
Return Values
If the method succeeds, the
return value is DS_OK. If the method fails, the return value may be one
of the
following error values:
Return code |
DSERR_CONTROLUN***AIL |
DSERR_GENERIC |
DSERR_INVALIDPARAM |
DSERR_PRIOLEVELNEEDED |
Remarks
Allowable values are between
DSBVOLUME_MAX (no attenuation) and DSBVOLUME_MIN (silence). These values
are
defined in Dsound.h as 0 and ?10,000 respectively. The value
DSBVOLUME_MAX
represents the original, unadjusted volume of the stream. The value
DSBVOLUME_MIN indicates an audio volume attenuated by 100 dB, which, for
all
practical purposes, is silence. DirectSound does not support
amplification.
以下给出一个完整示例来运用刚才学到的知识。
点击下载源码和工程
源码示例:
/***************************************************************************************
PURPOSE:
Lock Load Playing Demo
***************************************************************************************/
#define
DIRECTINPUT_VERSION 0x0800
#include <windows.h>
#include <stdio.h>
#include <dsound.h>
#include "resource.h"
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dsound.lib")
#pragma warning(disable : 4996)
#define
Safe_Release(p) if((p)) (p)->Release();
// window handles, class and caption text.
HWND g_hwnd;
char
g_class_name[] = "LockLoadClass";
IDirectSound8* g_ds;
// directsound component
IDirectSoundBuffer8* g_ds_buffer;
// sound buffer object
//--------------------------------------------------------------------------------
// Window procedure.
//--------------------------------------------------------------------------------
long
WINAPI Window_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch
(msg)
{
case
WM_DESTROY:
PostQuitMessage(0);
return
0;
}
return
(
long
) DefWindowProc(hwnd, msg, wParam, lParam);
}
//--------------------------------------------------------------------------------
// Main function, routine entry.
//--------------------------------------------------------------------------------
int
WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line,
int
cmd_show)
{
WNDCLASS win_class;
MSG msg;
IDirectSoundBuffer* ds = NULL;
// create window class and register it
win_class.style = CS_HREDRAW | CS_VREDRAW;
win_class.lpfnWndProc = Window_Proc;
win_class.cbClsExtra = 0;
win_class.cbWndExtra = DLGWINDOWEXTRA;
win_class.hInstance = inst;
win_class.hIcon = LoadIcon(inst, IDI_APPLICATION);
win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
win_class.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
win_class.lpszMenuName = NULL;
win_class.lpszClassName = g_class_name;
if
(! RegisterClass(&win_class))
return
FALSE;
// create the main window
g_hwnd = CreateDialog(inst, MAKEINTRESOURCE(IDD_LOCKLOAD), 0, NULL);
ShowWindow(g_hwnd, cmd_show);
UpdateWindow(g_hwnd);
// initialize and configure directsound
// creates and initializes an object that supports the IDirectSound8 interface
if
(FAILED(DirectSoundCreate8(NULL, &g_ds, NULL)))
{
MessageBox(NULL, "Unable to create DirectSound object", "Error", MB_OK);
return
0;
}
// set the cooperative level of the application for this sound device
g_ds->SetCooperativeLevel(g_hwnd, DSSCL_NORMAL);
// create a sound buffer
// setup the W***EFORMATEX structure
W***EFORMATEX wave_format;
ZeroMemory(&wave_format,
sizeof
(W***EFORMATEX));
wave_format.wFormatTag = W***E_FORMAT_PCM;
wave_format.nChannels = 1;
// mono
wave_format.nSamplesPerSec = 11025;
wave_format.wBitsPerSample = 16;
wave_format.nBlockAlign = (wave_format.wBitsPerSample / 8) * wave_format.nChannels;
wave_format.nAvgBytesPerSec = wave_format.nSamplesPerSec * wave_format.nBlockAlign;
// setup the DSBUFFERDESC structure
DSBUFFERDESC ds_buffer_desc;
// zero out strcutre
ZeroMemory(&ds_buffer_desc,
sizeof
(DSBUFFERDESC));
ds_buffer_desc.dwSize =
sizeof
(DSBUFFERDESC);
ds_buffer_desc.dwFlags = DSBCAPS_CTRLVOLUME;
ds_buffer_desc.dwBufferBytes = wave_format.nAvgBytesPerSec * 2;
// 2 seconds
ds_buffer_desc.lpwfxFormat = &wave_format;
// create the fist version object
if
(FAILED(g_ds->CreateSoundBuffer(&ds_buffer_desc, &ds, NULL)))
{
// error ocuurred
MessageBox(NULL, "Unable to create sound buffer", "Error", MB_OK);
}
else
{
// get the version 8 interface
ds->QueryInterface(IID_IDirectSoundBuffer8, (
void
**)&g_ds_buffer);
// release the original interface
ds->Release();
// lock buffer, fill with random values, and unlock.
char
* ptr;
DWORD size;
// readies all or part of the buffer for a data write and returns pointers to which data can be written
if
(SUCCEEDED(g_ds_buffer->Lock(0, 0, (
void
**)&ptr, &size, NULL, 0, DSBLOCK_ENTIREBUFFER)))
{
for
(DWORD i = 0; i < size; i++)
ptr[i] = rand() % 256;
}
// releases a locked sound buffer
g_ds_buffer->Unlock(ptr, size, NULL, 0);
// play sound looping
// sets the position of the play cursor,
// which is the point at which the next byte of data is read from the buffer.
g_ds_buffer->SetCurrentPosition(0);
// set the attenuation of the sound
g_ds_buffer->SetVolume(DSBVOLUME_MAX);
// causes the sound buffer to play, starting at the play cursor.
g_ds_buffer->Play(0, 0, DSBPLAY_LOOPING);
}
// start message pump, waiting for signal to quit.
ZeroMemory(&msg,
sizeof
(MSG));
while
(msg.message != WM_QUIT)
{
if
(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// release directsound objects
g_ds->Release();
UnregisterClass(g_class_name, inst);
return
(
int
) msg.wParam;
}
运行截图:
阅读下篇:用
DirectX
Audio和DirectShow播放声音和音乐(3)
相关文章推荐
- 用DirectX Audio和DirectShow播放声音和音乐(1)
- 用DirectX Audio和DirectShow播放声音和音乐(7)
- 用DirectX Audio和DirectShow播放声音和音乐(3)
- 用DirectX Audio和DirectShow播放声音和音乐(4)
- 用DirectX Audio和DirectShow播放声音和音乐(5)
- 用DirectX Audio和DirectShow播放声音和音乐(6)
- 用DirectX Audio和DirectShow播放声音和音乐(1)
- 用DirectX Audio和DirectShow播放声音和音乐(7)
- 用DirectX Audio和DirectShow播放声音和音乐
- 用DirectX Audio和DirectShow播放声音和音乐(2)
- 用DirectX Audio和DirectShow播放声音和音乐(5)
- 用DirectX Audio和DirectShow播放声音和音乐(6)
- 用DirectX Audio和DirectShow播放声音和音乐(6)
- 用DirectX Audio和DirectShow播放声音和音乐(1)
- 用DirectX Audio和DirectShow播放声音和音乐(7)
- 用DirectX Audio和DirectShow播放声音和音乐(2)
- 用DirectX Audio和DirectShow播放声音和音乐(3)
- 用DirectX Audio和DirectShow播放声音和音乐(7)
- 用DirectX Audio和DirectShow播放声音和音乐(5)
- 用DirectX Audio和DirectShow播放声音和音乐(3)