频道直达 - 专题 - 新闻 - 技巧 - 组网 - 开发 - 安全 - web编程 - 图像 - 操作系统 - 数据库 - 教育 - 旅游 - 健康 - 时尚 - 驱动 - 软件 - 游戏 - 多媒体 - ERP - 讨论组

DirectSound应用程序开发快速入门

来源: 作者: 出处:巧巧读书 2006-02-06 进入讨论组
上一页 1 2 
四、使用静态的缓冲区

  如果我们的wave文件不是很大,那么我们就可以使用静态的缓冲区了。

  包含全部音频数据的缓冲区我们称为静态的缓冲区,尽管,不同的声音可能会反复使用同一个内存buffer,但严格来说,静态缓冲区的数据只写入一次。

  静态缓冲区的创建和管理和流缓冲区很相似,唯一的区别就是它们使用的方式不一样,静态缓冲区只填充一次数据,然后就可以play,然而,流缓冲区是一边play,一边填充数据。

  给静态缓冲区加载数据分下面几个步骤

  1、调用IDirectSoundBuffer8::Lock函数来锁定所有的内存,你要指定你锁定内存中你开始写入数据的偏移位置,并且取回该偏移位置的地址。

  2、采用标准的数据copy方法,将音频数据复制到返回的地址。

  3、调用IDirectSoundBuffer8::Unlock.,解锁该地址。

  下面我给出使用static buffer 播放wav文件的完整代码,首先定义我们需要的一些对象:

LPDIRECTSOUNDBUFFER8 g_pDSBuffer8 = NULL; //buffer
LPDIRECTSOUND8 g_pDsd = NULL; //dsound
CWaveFile *g_pWaveFile= NULL;
//下面初始化DirectSound工作。
HRESULT hr;
if(FAILED(hr = DirectSoundCreate8(NULL,&g_pDsd,NULL)))
 return FALSE;
 //设置设备的协作度
if(FAILED(hr = g_pDsd->SetCooperativeLevel(m_hWnd,DSSCL_PRIORITY)))
 return FALSE;

g_pWaveFile = new CWaveFile;
g_pWaveFile->Open(_T("d:\\test.wav"),NULL,WAVEFILE_READ);
DSBUFFERDESC dsbd;
ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLFX| DSBCAPS_CTRLPOSITIONNOTIFY |DSBCAPS_GETCURRENTPOSITION2;
dsbd.dwBufferBytes = g_pWaveFile->GetSize();//MAX_AUDIO_BUF * BUFFERNOTIFYSIZE ;
dsbd.lpwfxFormat = g_pWaveFile->m_pwfx;
LPDIRECTSOUNDBUFFER lpbuffer;
//创建辅助缓冲区对象
if(FAILED(hr = g_pDsd->CreateSoundBuffer(&dsbd,&lpbuffer,NULL)))
 return ;
if( FAILED( hr = lpbuffer->QueryInterface( IID_IDirectSoundBuffer8, (LPVOID*) &g_pDSBuffer8) ) )
 return ;
lpbuffer->Release();
//准备工作做完了,下面就开始播放了
LPVOID lplockbuf;
DWORD len;
DWORD dwWrite;

g_pDSBuffer8->Lock(0,0,&lplockbuf,&len,NULL,NULL,DSBLOCK_ENTIREBUFFER);
g_pWaveFile->Read((BYTE*)lplockbuf,len,&dwWrite);
g_pDSBuffer8->Unlock(lplockbuf,len,NULL,0);
g_pDSBuffer8->SetCurrentPosition(0);
g_pDSBuffer8->Play(0,0,DSBPLAY_LOOPING);

  五、使用流缓冲区播放超大型的wave文件

  流缓冲区用来播放那些比较长的音频文件,因为数据比较长,没法一次填充到缓冲区中,一边播放,一边将新的数据填充到DirectSound的缓冲区中。

  可以通过IDirectSoundBuffer8::Play函授来播放缓冲区中的内容,注意在该函数的参数中一定要设置DSBPLAY_LOOPING标志。

  通过IDirectSoundBuffer8::Stop方法中断播放,该方法会立即停止缓冲区播放,因此你要确保所有的数据都被播放,你可以通过拖动播放位置或者设置通知位置来实现。

  将音频流倒入缓冲区需要下面三个步骤

  1、确保你的缓冲区已经做好接收新数据的准备。你可以拖放播放的光标位置或者等待通知

  2、调用IDirectSoundBuffer8::Lock.函数锁住缓冲区的位置,这个函数返回一个或者两个可以写入数据的地址

  3、使用标准的copy数据的方法将音频数据写入缓冲区中

  4、IDirectSoundBuffer8::Unlock.,解锁

  这里我要讲一下DirectSound的通知机制。因为Stream buffer 大小只够容纳一部分数据,因此,在播放完缓冲区中的数据后,DirectSound就会通知应用程序,将新的数据填充到DirectSound的缓冲区中。假如我们设置DirectSound的buffersize 为1920*4,如下图

DirectSound应用程序开发快速入门(图二)

  我们可以给DirectSound设置一个事件,并且设置buffer通知大小,如下:

HANDLE g_event[MAX_AUDIO_BUF];
for(int i =0; i< MAX_AUDIO_BUF;i++)
{
 g_aPosNotify[i].dwOffset = i* BUFFERNOTIFYSIZE ;
 g_aPosNotify[i].hEventNotify = g_event[i];
}
if(FAILED(hr = g_pDSBuffer8->QueryInterface(IID_IDirectSoundNotify,(LPVOID *) &g_pDSNotify )))
 return ;
g_pDSNotify->SetNotificationPositions(MAX_AUDIO_BUF,g_aPosNotify);
g_pDSNotify->Release();

  当DirectSound播放到buffer的1920,3840,5760,7680等位置时,Directsound就会通知应用程序,将g_ event,设置为通知态,应用程序就可以通过WaitForMultipleObjects 函数等待DirectSound的通知,将数据填充到DirectSoun的辅助缓冲区。

  下面我给出Stream buffer 播放wave文件的代码。

#define MAX_AUDIO_BUF 4
#define BUFFERNOTIFYSIZE 1920
LPDIRECTSOUNDBUFFER8 g_pDSBuffer8 = NULL; //buffer
LPDIRECTSOUND8 g_pDsd = NULL; //dsound
CWaveFile *g_pWaveFile= NULL;
BOOL g_bPlaying = FALSE; //是否正在播放
LPDIRECTSOUNDNOTIFY8 g_pDSNotify = NULL;
DSBPOSITIONNOTIFY g_aPosNotify[MAX_AUDIO_BUF];//设置通知标志的数组
HANDLE g_event[MAX_AUDIO_BUF];
DWORD g_dwNextWriteOffset = 0;

//初始化DirectSound
HRESULT hr;
if(FAILED(hr = DirectSoundCreate8(NULL,&g_pDsd,NULL)))
return FALSE;
if(FAILED(hr = g_pDsd->SetCooperativeLevel(m_hWnd,DSSCL_PRIORITY)))
return FALSE;

g_pWaveFile = new CWaveFile;
g_pWaveFile->Open(_T("d:\\test.wav"),NULL,WAVEFILE_READ);

DSBUFFERDESC dsbd;
ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY |DSBCAPS_GETCURRENTPOSITION2;
dsbd.dwBufferBytes = MAX_AUDIO_BUF * BUFFERNOTIFYSIZE ;
dsbd.lpwfxFormat = g_pWaveFile->m_pwfx;
LPDIRECTSOUNDBUFFER lpbuffer;
//创建DirectSound辅助缓冲区
if(FAILED(hr = g_pDsd->CreateSoundBuffer(&dsbd,&lpbuffer,NULL)))
 return FALSE;
if( FAILED( hr = lpbuffer->QueryInterface( IID_IDirectSoundBuffer8, (LPVOID*) &g_pDSBuffer8) ) )
 return FALSE;
lpbuffer->Release();
//设置DirectSound通知 机制
for(int i =0; i< MAX_AUDIO_BUF;i++)
{
 g_aPosNotify[i].dwOffset = i* BUFFERNOTIFYSIZE ;
 g_aPosNotify[i].hEventNotify = g_event[i];
}
if(FAILED(hr=g_pDSBuffer8->QueryInterface(IID_IDirectSoundNotify,(LPVOID*) g_pDSNotify )))
 return ;
g_pDSNotify->SetNotificationPositions(MAX_AUDIO_BUF,g_aPosNotify);
g_pDSNotify->Release();
ok,在下面的play函数中,我们就要单独启动一个线程,来播放了
void OnBnClickedButtonPlay()
{
 g_bPlaying =TRUE;
 g_pWaveFile->ResetFile();
 CreateThread(0,0,PlayThread,this,NULL,NULL);
}
//停止播放音频
void CDsoundEffectDemoDlg::OnBnClickedButtonStop()
{
 // TODO: 在此添加控件通知处理程序代码
 g_bPlaying =FALSE;
 Sleep(500);
 g_pDSBuffer8->Stop();
}

  下面我们看看我们的播放线程,在线程里,我们首先将音频数据填充到DirectSound的辅助缓冲区中,然后调用DirectSound buffer 的play方法,开始播放,然后就在WaitForMultipleObjects 等待DirectSound的通知吧,然后读取wave文件将数据填充到DirectSound的空buffer中。

DWORD WINAPI PlayThread(LPVOID lpParame)
{
 DWORD res;
 LPVOID lplockbuf;
 DWORD len;
 DWORD dwWrite;

 g_pDSBuffer8->Lock(0,0,&lplockbuf,&len,NULL,NULL,DSBLOCK_ENTIREBUFFER);
 g_pWaveFile->Read((BYTE*)lplockbuf,len,&dwWrite);
 g_pDSBuffer8->Unlock(lplockbuf,len,NULL,0);
 g_pDSBuffer8->SetCurrentPosition(0);
 g_pDSBuffer8->Play(0,0,DSBPLAY_LOOPING);
 g_dwNextWriteOffset = 0;
 while(g_bPlaying)
 {
  res = WaitForMultipleObjects (MAX_AUDIO_BUF, g_event, FALSE, INFINITE);
  if(res > WAIT_OBJECT_0)
   ProcessBuffer();
 }

 return 0;
}

  下面的函数主要是给空的DirectSound缓冲区填充 音频数据。

void ProcessBuffer()
{
 DWORD dwBytesWrittenToBuffer = 0;
 VOID* pDSLockedBuffer = NULL;
 VOID* pDSLockedBuffer2 = NULL;
 DWORD dwDSLockedBufferSize;
 DWORD dwDSLockedBufferSize2;
 HRESULT hr;
 g_pDSBuffer8->Lock(g_dwNextWriteOffset,BUFFERNOTIFYSIZE,&pDSLockedBuffer,&dwDSLockedBufferSize, &pDSLockedBuffer2,&dwDSLockedBufferSize2,0);
 if(hr == DSERR_BUFFERLOST)
 {
  g_pDSBuffer8->Restore();
  g_pDSBuffer8->Lock(g_dwNextWriteOffset,BUFFERNOTIFYSIZE,&pDSLockedBuffer,&dwDSLockedBufferSize,
&pDSLockedBuffer2,&dwDSLockedBufferSize2,0);
 }
 if(SUCCEEDED(hr))
 {
  //write
  g_pWaveFile->Read((BYTE*)pDSLockedBuffer,dwDSLockedBufferSize,&dwBytesWrittenToBuffer);
  g_dwNextWriteOffset += dwBytesWrittenToBuffer;

  if (NULL != pDSLockedBuffer2)
  {
   g_pWaveFile->Read((BYTE*)pDSLockedBuffer2,dwDSLockedBufferSize2,&dwBytesWrittenToBuffer);
   g_dwNextWriteOffset += dwBytesWrittenToBuffer;
  }
  g_dwNextWriteOffset %= (BUFFERNOTIFYSIZE * MAX_AUDIO_BUF);

  if(dwBytesWrittenToBuffer <BUFFERNOTIFYSIZE )
  {
   FillMemory( (BYTE*) pDSLockedBuffer + dwBytesWrittenToBuffer,
   BUFFERNOTIFYSIZE - dwBytesWrittenToBuffer,
(BYTE)(g_pWaveFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );

   g_bPlaying = FALSE;
  }
  hr = g_pDSBuffer8->Unlock(pDSLockedBuffer,dwDSLockedBufferSize,
  pDSLockedBuffer2,dwDSLockedBufferSize2);
 }
}

  六、结束语

  本文介绍DirectSound开发的入门知识,开发环境的配置,并通过实例代码介绍了DirectSound播放音频文件的两种方法。留下email,aooang@hotmail.com有什么问题,欢迎和大家交流。专题:http://www.qqread.com/vc/q112257200.html 更多文章 更多内容请看Solaris基础知识入门Wlan组网----家庭专题专题,或进入讨论组讨论。
上一页 1 2 
收藏此文】【 】【打印】【关闭
相关图文阅读
频道图文推荐
健 康 咨 询
时 尚 咨 询
巧巧读书宗旨
相关专题
讨论组问题推荐
站内各频道最新更新文档
站内最新制作专题
热门关键字导读
Photoshop教 程照片处理 照片制作 PS快捷键 抠图
计 算 机 故 障XP系统修复
艺 术 与 设 计设计 流媒体 设计欣赏 边框
计 算 机 安 全ARP
站内频道文章精选
巧巧电脑频道编辑信箱  告诉我们您想看的专题或文章