您现在的位置:首页 >> 图形媒体 >> 图形媒体 >> 内容

DirectSound学习笔记之静态缓冲区播放

时间:2011/9/3 15:02:16 点击:

  核心提示:DirectSound之 播放 前面,学习到了创建播放设备,目的自然就是为了播放声音嘛! DirectSound有一个缓冲区对象IDirectSoundBuffer,该对象表示一个包含数据的缓冲区,这...

DirectSound之 播放
   前面,学习到了创建播放设备,目的自然就是为了播放声音嘛!
   DirectSound有一个缓冲区对象IDirectSoundBuffer,该对象表示一个包含数据的缓冲区,这些数据以PCM格式被存储,该对象不仅可以用于开始、停止或暂停声音的播放,还能够设置声音数据中诸如频率和格式等属性。 缓冲区分为主缓冲区和副缓冲区,主缓冲区中是听者将要听到的音频信号,一般是将副缓冲区中信号混音后的结果。而副缓冲区中存放着许多单独的声音信号,有的可以直接播放,有的要混音,有的循环播放。主缓冲区由DirectSound自动创建,而副缓冲区需由应用程序来创建。DirectSound将副缓冲区中的声音混合后,存入主缓冲区,再输出到相应播放设备。
   DirectSound中没有解析声音文件的功能,需要自己在应用程序中将不同格式的声音信号改变过来(PCM) 。缓冲区可以在主板的RAM、波表存储器、DMA通道或虚拟存储器中。 多个应用程序可以用同一声音设备来创建DirectSound对象。当输入焦点在应用程序中发生变化时,音频输出将自动在各个应用程序之间切换。于是,应用程序不用在输入焦点改变中反复的
播放和停止他们的缓冲区。通过IDirectSoundNotify接口,当播放到了一个用户指定的地方,或播放结束时,DirectSound将动态地通知拥护这一事件。

   上面说了DirectSound自动创建主缓冲区,副缓冲区,要自己创建。
   应用程序必须至少创建一个副缓冲区,用来存储要播放的声音数据文件。一个副缓冲区的生命期可以比应用程序还长,所以在不需要的时候,应该要将其释放,一个副缓冲区可以包含整个声音数据,也可以只包含声音数据的一部分,为了限制内存的开销,所以一般在播放长文件时采用流缓冲区,这些流缓冲区中只用包含几秒钟的数据量。
    可以通过同时播放几个副缓冲区中的声音来对他们进行混音,至于能同时播放几个副缓冲区
则由硬件设备的性能决定。另外要注意的是,不能创建相同的副缓冲区,副缓冲区的格式一般为
Format:    缓冲区的format必须要和播放音频的waveformat一致
Controls:  不同的缓冲区可以有不同的配置,如音量,频率等。
Location:  缓冲区时是硬件来管理的还是由软件来管理的。硬件缓冲区通常很有效率,但是他不
支持64位的操作系统
   这里咱给一个例子创建一个副缓冲区

function CreatePlayBuffer: Boolean;
var
  wfx: tWAVEFORMATEX;//要申请的波形格式,这个需要引用MMSystem单元
  DsbufferDesc: TDSBufferDesc;//目标缓存区
begin
  ZeroMemory(@Wfx,SizeOf(wfx));
  wfx.wFormatTag := WAVE_FORMAT_PCM;{指定格式类型; 默认 WAVE_FORMAT_PCM = 1}
  wfx.nChannels := 2; {指出波形数据的通道数; 单声道为 1, 立体声为 2}
  wfx.nSamplesPerSec := 44100; {指定样本速率(每秒的样本数)一般为8000}
  wfx.nBlockAlign := 4; {指定块对齐(单位字节), 块对齐是数据的最小单位}
  wfx.nAvgBytesPerSec := wfx.nSamplesPerSec*wfx.nBlockAlign;{指定数据传输的平均速率(每秒的字节数)每秒的字节数:}
  wfx.wBitsPerSample := 16 ;{采样大小(字节)每个样本的BIT数目,一般为16}
  wfx.cbSize := SizeOf(wfx);

  ZeroMemory(@DsbufferDesc,SizeOf(TDsBufferDesc));
  DsbufferDesc.dwSize := SizeOf(TDsBufferDesc);
  DsbufferDesc.dwFlags := DSBCAPS_CTRLPAN or DSBCAPS_CTRLVOLUME or DSBCAPS_CTRLFREQUENCY or DSBCAPS_GLOBALFOCUS ;
  DsbufferDesc.dwBufferBytes := wfx.nSamplesPerSec*40;//创建一个40秒的缓冲区
  DsbufferDesc.lpwfxFormat := @wfx;
  Result := Succeeded(lp.CreateSoundBuffer(DsbufferDesc,SoundBuffer,nil));
end;

 

创建需要使用IDirectSound8.CreateSoundBuffer函数,该函数的第一个参数为一个DSBUFFERDESC
结构,使用之前,一定要给定一个长度,也就是要为dwsize设置一个值:
DsbufferDesc.dwSize := SizeOf(TDsBufferDesc);
dwFlags指定了应用程序对该缓冲区的控制属性,dwFlags的取值为:
DSBCAPS_CTRL3D               声音可以在3个方向上进行移动
DSBCAPS_CTRLFX               可以在缓冲区中添加特技
DSBCAPS_CTRLFREQUENCY        声音的频率可以被改动
DSBCAPS_CTRLPAN              声音可以从左声道被移动到右声道
DSBCAPS_CTRLPOSITIONNOTIFY   可以在Buffer中设置通知的位置
DSBCAPS_CTRLVOLUME           声音的大小可以控制
DSBCAPS_GLOBALFOCUS          窗口在失去焦点时,缓冲区依然能够播放
需要注意的是,有些标志位的组合是不允许的。
上面的代码中,创建了一个3秒的数据缓冲区

   如果缓冲区的位置没有指定,DirectSound在条件允许的情况下将你的缓冲区设置为硬件缓冲控制,因为硬件缓冲区的混音通过声卡加速器

进行,受应用程序影响小。
   如果想自己控制创建的缓冲区位置,一定要将dwFlags标志中设置为DSBCAPS_LOCHARDWARE或者
设置为DSBCAPS_LOCSOFTWARE,如果设置为DSBCAPS_LOCHARDWARE,此时硬件设备的资源不足时,创
建将失败,如果想使用DirectSound的管理声音特性,则要指定DSBCAPS_LOCDEFER标志,该标志表
示只有在播放的时候才分配内存。通过IDirectSoundBuffer8.GetCaps来探明已经存的dwflags设置状况
设置音量,可以使用IDirectSoundBuffer8.SetValue来实现,但是前提是只有在创建Buffer的时
候设置了DSBCAPS_CTRLVOLUME标志,该函数才能调用成功。
上面缓冲区创建好了之后,我们就可以向缓冲区中写入需要播放的数据,然后进行播放了。
填充静态缓冲区
  副缓冲区包含完整的声音数据就叫做静态缓冲区,虽然,和其他缓冲区没什么不同,但是数据
却只写进去一次。静态缓冲区的创建和管理很像流式缓冲区,他们不同的是在使用上,静态缓冲
区只填充一次然后去使用,但是流式缓冲区是需要不断用欲播放的数据刷新。
  注: 静态缓冲区是不需要在创建时设置DSBCAPS_STATIC标志去刻画缓冲区的,该标志要求声卡
分配内存,在大多数硬件上是可以的。静态缓冲区可以存在系统内存内,创建时加上
DSBCAPS_LOCHARDWARE或DSBCAPS_LOCSOFTWARE标志
  给静态缓冲区装载数据需要三个过程:
  1. 调用IDirectSoundBuffer8.Lock将缓冲区锁住,指出要写入的偏移位置,然后得到写入的内
存地址。
  2. 通过标准的内存复制机制将声音数据写入到第一步中获得的欲写入的内存地址。
  3. 调用IDirectSoundBuffer.UnLock解锁
在DsPack中IDirectSoundBuffer8的Lock声明如下:

 

function Lock(dwOffset, dwBytes: DWORD; out ppvAudioPtr1: Pointer; out pdwAudioBytes1: DWORD;
      out ppvAudioPtr2: Pointer; out pdwAudioBytes2: DWORD; dwFlags: DWORD): HResult; stdcall;

 

function TForm2.FillPlayBuffer: Boolean;
var
  ToWritePointe,WrapPointer: Pointer;
  dwLen,WrapLen: DWORD;//写入的长度
  st: TMemoryStream;
begin
  SoundBuffer.Lock(0,//开始写入数据的偏移
                   0,
                   ToWritePointe,//要写入的缓冲区位置地址
                   dwLen,//长度
                   WrapPointer,WrapLen,DSBLOCK_ENTIREBUFFER);
  //开始写入数据
  st := TMemoryStream.Create;
  st.LoadFromFile('C:\st8.wav');
  CopyMemory(ToWritePointe,st.Memory,dwLen);//写入数据到缓冲区
  SoundBuffer.Unlock(ToWritePointe,dwLen,nil,0);
  st.Free;
end;

这里填充好了静态缓冲区了,然后要播放的话,很容易
直接在末尾加一个
SoundBuffer.Play(0,0,0)就可以开始播放了

作者:不得闲 来源:转载
共有评论 0相关评论
发表我的评论
  • 大名:
  • 内容:
本类推荐
  • 没有
本类固顶
  • 没有
  • 盒子文章(www.2ccc.com) © 2022 版权所有 All Rights Reserved.
  • 沪ICP备05001939号