`
xmq315nv
  • 浏览: 18335 次
社区版块
存档分类
最新评论

DirectSound开发指南(四)

 
阅读更多

DirectSound开发指南(四)
2011年07月08日
  3.6录制Capturing Waveforms
  1枚举录音的设备
  如果你的程序只是想从用户缺省的设备上进行声音的录制,那么就没有必要来枚举出系统中的所有录音的设备,当你调用DirectSoundCaptureCreate8 或者另外一个函数
  DirectSoundFullDuplexCreate8的时候,其实就默认指定了一个缺省的录音设备。
  当然,在下面的情况下,你就必须要枚举系统中所有的设备,
  DWORD pv;  // Can be any 32-bit type.
  HRESULT hr = DirectSoundCaptureEnumerate(
  (LPDSENUMCALLBACK)DSEnumProc, (VOID*)&pv);
  2创建设备对象
  你可以通过DirectSoundCaptureCreate8或者DirectSoundFullDuplexCreate8函数直接创建设备对象,该函数返回一个指向IDirectSoundCapture8接口的指针
  3录音设备的性能
  你可以通过IDirectSoundCapture8::GetCaps方法来获取录音的性能,这个函数的参数是一个DSCCAPS类型的结构,在传递这个参数之前,一定要初始化该结构的dwSize成员变量。同时,你可以通过这个结构返回设备支持的声道数,以及类似WAVEINCAPS结构的其他设备属性
  4创建录音的buffer
  我们可以通过IDirectSoundCapture8::CreateCaptureBuffer来创建一个录音的buffer对象,这个函数的一个参数采用DSCBUFFERDESC类型的结构来说明buffer的一些特性,这个结构的最后一个成员变量是一个WAVEFORMATEX结构,这个结构一定要初始化成泥需要的wav格式。
  说明一下,如果你的应用程序一边播放的同时进行录制,如果你录制的buffer格式和你的主缓冲buffer不一样,那么你创建录制buffer对象就会失败,原因在于,有些声卡只支持一种时钟,不能同时支持录音和播放同时以两种不同的格式进行。
  下面的函数,演示了如何创建一个录音的buffer对象,这个buffer对象能够处理1秒的数据,注意,这里传递的录音设备对象参数一定要通过DirectSoundCaptureCreate8来创建,而不是早期的DirectSoundCaptureCreate接口,否则,buffer对象不支持IDirectSoundCaptureBuffer8接口,
  HRESULT CreateCaptureBuffer(LPDIRECTSOUNDCAPTURE8 pDSC,
  LPDIRECTSOUNDCAPTUREBUFFER8* ppDSCB8)
  {
  HRESULT hr;
  DSCBUFFERDESC               dscbd;
  LPDIRECTSOUNDCAPTUREBUFFER  pDSCB;
  WAVEFORMATEX                wfx =
  {WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0};
  // wFormatTag, nChannels, nSamplesPerSec, mAvgBytesPerSec,
  // nBlockAlign, wBitsPerSample, cbSize
  if ((NULL == pDSC) || (NULL == ppDSCB8)) return E_INVALIDARG;
  dscbd.dwSize = sizeof(DSCBUFFERDESC);
  dscbd.dwFlags = 0;
  dscbd.dwBufferBytes = wfx.nAvgBytesPerSec;
  dscbd.dwReserved = 0;
  dscbd.lpwfxFormat = &wfx;
  dscbd.dwFXCount = 0;
  dscbd.lpDSCFXDesc = NULL;
  if (SUCCEEDED(hr = pDSC->CreateCaptureBuffer(&dscbd, &pDSCB, NULL)))
  {
  hr = pDSCB->QueryInterface(IID_IDirectSoundCaptureBuffer8, (LPVOID*)ppDSCB8);
  pDSCB->Release(); 
  }
  return hr;
  }
  5通过录音buffer对象获取信息
  你可以通过IDirectSoundCaptureBuffer8::GetCaps方法来获取录音buffer的大小,但一定要记得初始化DSCBCAPS结构类型参数的dwSize成员变量。
  为了获取buffer中数据的格式,你可以通过IDirectSoundCaptureBuffer8::GetFormat.方法来获取buffer中的数据格式,这个函数通过WAVEFORMATEX结构返回音频数据的信息,
  如果我们想知道一个录音buffer目前的状态如何,可以通过IDirectSoundCaptureBuffer8::GetStatus来获取,这个函数通过一个DWORD类型的参数来表示该buffer是否正在录音,
  IDirectSoundCaptureBuffer8::GetCurrentPosition方法可以获取buffer中read指针和录制指针的偏差。Read指针指向填充到该buffer中的数据的最末端,capture指针则指向复制到硬件的数据的末端,你read指针指向的前段数据都是安全数据,你都可以安全的复制。
  6录音buffer对象通知机制
  为了安全的定期的从录音buffer中copy数据,你的应用程序就要知道,什么时候read指针指向了特定的位置,一个方法是通过IDirectSoundCaptureBuffer8::GetCurrentPosition.方法来获取read指针的位置,另外一个更有效的方法采用通知机制,通过IDirectSoundNotify8::SetNotificationPositions方法,你可以设置任何一个小于buffer的位置
  来触发一个事件,切记,当buffer正在running的时候,不要设置。
  如何来设置一个触发事件呢,首先要得到IDirectSoundNotify8接口指针,你可以通过buffer对象的QuerInterface来获取这个指针接口,对于你指定的任何一个position,你都要通过CreateEvent方法,创建一个win32内核对象, 然后将内核对象的句柄赋给DSBPOSITIONNOTIFY结构的hEventNotify成员,通过该结构的dwOffset来设置需要通知的位置在buffer中的偏移量。
  最后将这个结构或者结构数组,传递给SetNotificationPositions函数,下面的例子设置了三个通知,当position达到buffer的一半时会触发一个通知,当达到buffer的末端时,触发第二个通知,当录音停止时触发第三个通知消息。
  HRESULT SetCaptureNotifications(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB)
  {
  #define cEvents  3
  LPDIRECTSOUNDNOTIFY8 pDSNotify;
  WAVEFORMATEX         wfx; 
  HANDLE     rghEvent[cEvents] = {0};
  DSBPOSITIONNOTIFY  rgdsbpn[cEvents];
  HRESULT    hr;
  if (NULL == pDSCB) return E_INVALIDARG;
  if (FAILED(hr = pDSCB->QueryInterface(IID_IDirectSoundNotify, (LPVOID*)&pDSNotify)))
  {
  return hr;
  }
  if (FAILED(hr = pDSCB->GetFormat(&wfx, sizeof(WAVEFORMATEX), NULL)))
  {
  return hr;
  }
  // Create events.
  for (int i = 0; i SetNotificationPositions(cEvents, rgdsbpn);
  pDSNotify->Release();
  return hr;
  }
  7录音的步骤
  录音主要包括下面几个步骤
  1调用IDirectSoundCaptureBuffer8::Start使buffer对象开始工作,通过你要给这个函数dwFlags传递一个DSCBSTART_LOOPING参数,注意,buffer就会不停工作,而不是当buffer填充满了就停止工作,当缓冲区满了后,就会从头重新填充
  2等待你期望的事件通知,当buffer被填充到某个你期望的位置时,会触发通知。
  3当你收到通知的时,你就要调用IDirectSoundCaptureBuffer8::Lock来锁住bufer的一部份,切记,不要将capture指针指向的内存锁住,你可以调用IDirectSoundCaptureBuffer8::GetCurrentPosition方法来获取read指针的位置。在传递给Lock函数的参数中,你一定要指定内存的大小和偏移量,这个函数会返回你锁住的内存的起始地址,以及block的大小,
  4从你锁住的内存中复制data,
  5复制完成后要记得IDirectSoundCaptureBuffer8::Unlock方法来解锁内存
  6在你停止录音之前,你可以反复的重复2~5步骤,如果你想停止录音了,你可以调用
  IDirectSoundCaptureBuffer8::Stop方法。
  9将录音写入wav文件
  WAV文件是采用RIFF格式的文件,在文件中包含一系列的chunks,来描述头信息和数据信息,win32API提供一套mmio系列函数用来操作RIFF格式的文件,但是Directsound并没有提供读写wav格式文件的函数,但是,Directsound里封装了一个CWaveFile类用来操作wav文件,可以通过open来写入文件的头信息,write来写入文件的数据,close函数写入文件的长度,关闭文件。
  下面是代码,如何创建一个wav格式的文件
  CWaveFile   g_pWaveFile;
  WAVEFORMATEX  wfxInput;
  ZeroMemory( &wfxInput, sizeof(wfxInput));
  wfxInput.wFormatTag = WAVE_FORMAT_PCM;
  wfxInput.nSamplesPerSec = 22050
  wfxInput.wBitsPerSample =  8;
  wfxInput.nChannels = 1;
  wfxInput.nBlockAlign =
  wfxInput.nChannels * (wfxInput.wBitsPerSample /;
  wfxInput.nAvgBytesPerSec =
  wfxInput.nBlockAlign * wfxInput.nSamplesPerSec;
  g_pWaveFile = new CWaveFile;
  if (FAILED(g_pWaveFile->Open("mywave.wav", &wfxInput,
  WAVEFILE_WRITE)))
  {
  g_pWaveFile->Close();
  }
  下面的代码就演示了如何从buffer对象中将数据写入文件中
  HRESULT RecordCapturedData()
  {
  HRESULT hr;
  VOID* pbCaptureData  = NULL;
  DWORD dwCaptureLength;
  VOID* pbCaptureData2 = NULL;
  DWORD dwCaptureLength2;
  VOID* pbPlayData   = NULL;
  UINT  dwDataWrote;
  DWORD dwReadPos;
  LONG lLockSize;
  if (NULL == g_pDSBCapture)
  return S_FALSE;
  if (NULL == g_pWaveFile)
  return S_FALSE;
  if (FAILED (hr = g_pDSBCapture->GetCurrentPosition(
  NULL, &dwReadPos)))
  return hr;
  // Lock everything between the private cursor
  // and the read cursor, allowing for wraparound.
  lLockSize = dwReadPos - g_dwNextCaptureOffset;
  if( lLockSize Lock(
  g_dwNextCaptureOffset, lLockSize,
  &pbCaptureData, &dwCaptureLength,
  &pbCaptureData2, &dwCaptureLength2, 0L)))
  return hr;
  // Write the data. This is done in two steps
  // to account for wraparound.
  if (FAILED( hr = g_pWaveFile->Write( dwCaptureLength,
  (BYTE*)pbCaptureData, &dwDataWrote)))
  return hr;
  if (pbCaptureData2 != NULL)
  {
  if (FAILED(hr = g_pWaveFile->Write(
  dwCaptureLength2, (BYTE*)pbCaptureData2,
  &dwDataWrote)))
  return hr;
  }
  // Unlock the capture buffer.
  g_pDSBCapture->Unlock( pbCaptureData, dwCaptureLength,
  pbCaptureData2, dwCaptureLength2  );
  // Move the capture offset forward.
  g_dwNextCaptureOffset += dwCaptureLength;
  g_dwNextCaptureOffset %= g_dwCaptureBufferSize;
  g_dwNextCaptureOffset += dwCaptureLength2;
  g_dwNextCaptureOffset %= g_dwCaptureBufferSize;
  return S_OK;
  }
  4DirectSound开发高级技巧
  4.1Dsound驱动模型(DirectSound Driver Models)
  在VXD驱动模型下,所有的DirectSound的混音工作都是由Dsound.vxd来完成的,一个虚拟的设备驱动程序。Dsound.vxd也提供操作声卡从Cpu接收数据的缓冲区的方法,这其实和DirectSound的主缓冲区是类似的。DirectSound应用程序可以给主缓冲区设置特定的属性,例如,采样频率,或者采样精度,也就改变了硬件设备。
  在WDM驱动模式下,DirectSound并不直接操作硬件,当然,操作硬件加速缓冲区除外。相应的,DirectSound将数据送往内核混音器,内核混音器的工作就是将多个格式的音频流调整为一个统一的格式,将它们进行混音,然后将结果送到硬件上进行播放。其实,它的工作和Dsound.vxd的工作类似。一个重要的区别在于Dsound.vxd仅仅对DirectSound的内存缓冲区进行混音,内核混音器会对所有的windows音频数据进行混音,包括那些使用waveOut win32函数输出数据的应用,DirectSound和Wave格式的音频输出设备不能同时打开在WDM驱动模式下是不成立的。
  最重要的是内核混音器和音频硬件的关系,内核混音器就是一个系统中的软件,可以用来指定硬件的DMA缓冲区。它根据它要进行混音的音频数据格式,来选择硬件的格式,它会将它混音的音频数据以一种高质量的形式进行输出,或者根据硬件的情况选择近似的音频质量。
  这里有一个很重要的暗示,就是DirectSound不能设置硬件的DMA缓冲区格式。对于你的应用程序而言,硬件格式其实就是根据你实际播放的音频的格式来定的。如果你play的是44kHZ,内核混音器就会将所有的数据都混音成44kHZ,同时也保证硬件以44Khz进行输出。
  作为应用程序的开发者,你没法来选择系统驱动模式,因为驱动模式的选择有声卡类型,windows版本,以及安装的驱动程序来控制。由于这个原因,你在测试你的程序时要注意所有的情况,DirectSound或许用的是Dsound.vxd或者用的是内核混音器,你要确保你的应用程序对两种方式都支持。
  4.2设置硬件的扩展属性(System Property Sets)
  4.3Property Sets for DirectSound Buffers
  DirectSound设备的属性可以通过下面的类对象来获取,该类的ID为CLSID_DirectSoundPrivate (11AB3EC0-25EC-11d1-A4D8-00C04FC28ACA). 该类支持IKsPropertySet接口,CLSID和属性的定义在Dsconf.h文件中可以找到。
  1要想创建该类的对象,首先要通过LoadLibrary加载Dsound.dll
  2调用GetProcAddress函数来获取DllGetClassObject函数接口。
  3通过DllGetClassObject函数来获取IClassFactory接口,CLSID_DirectSoudnPrivate类厂对象接口。
  4调用IClassFactory::CreateInstance来创建CLSID_DirectSoundPrivate对象,并获取IKsPropertySet接口(IID_IKsPropertySet)。
  CLSID_DirectSoundPrivate对象只支持一个属性设置接口DSPROPSETID_DirectSoundDevice(84624f82-25ec-11d1-a4d8-00c04fc28aca)这个属性设置接口暴露了下面三个只读的属性
  DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING
  DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
  DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE
  DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING这个属性根据指定的设备的名称来获取该设备的GUID。
  获取的属性数据包含在
  DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING_DATA结构里,这个结构包含下面的成员
  Member
  Type
  Description
  DeviceName
  String
  [in] Name of device
  DataFlow
  DIRECTSOUNDDEVICE_DATAFLOW
  [in] Direction of data flow, either DIRECTSOUNDDEVICE_DATAFLOW_RENDER or DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE
  DeviceID
  GUID
  [out] DirectSound device ID
  DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION属性
  该属性可以返回指定GUID设备的全部的属性描述,可以通过下面的结构返回数据
  DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA这个结构的成员如下
  Member
  Type
  Description
  Type
  DIRECTSOUNDDEVICE_TYPE
  [out] Device type: DIRECTSOUNDDEVICE_TYPE_EMULATED, DIRECTSOUNDDEVICE_TYPE_VXD or DIRECTSOUNDDEVICE_TYPE_WDM
  DataFlow
  DIRECTSOUNDDEVICE_DATAFLOW
  [in, out] Direction of data flow, either DIRECTSOUNDDEVICE_DATAFLOW_RENDER or DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE
  DeviceID
  GUID
  [in] DirectSound device GUID, or NULL for the default device of the type specified by DataFlow
  Description
  String
  [out] Description of DirectSound device
  Module
  String
  [out] Module name of the DirectSound driver
  Interface
  String
  [out] PnP device interface name
  WaveDeviceID
  ULONG
  [out] Identifier of the corresponding Windows Multimedia device
  DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE属性
  这个属性用来枚举所有的DirectSound的播放或者录音设备。可以通过
  DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_DATA结构将属性值返回,这个结构的成员如下
  Member
  Type
  Description
  Callback
  LPFNDIRECTSOUNDDEVICEENUMERATECALLBACK
  [in] Application-defined callback function. When IKsPropertySet::Get is called, this function is called once for each device enumerated. It takes as parameters a DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA structure describing the enumerated device, and the value in Context.
  Context
  LPVOID
  [in] User-defined value to be passed to the callback function for each device enumerated.
  4.4如何优化Directsound(Optimizing DirectSound Performance)
  主要讲三个方面的内容,如何用硬件进行混音,动态声音管理,有效地使用缓冲区
  许多声卡都拥有自己的辅助缓冲区,可以处理3-D特技和混音特技。这些缓冲区,就像它们的名字一样,其实是存在于系统的内存中,而不是在声卡上,但是这些缓冲区是由声卡来直接操作的,所以比需要处理器来控制软件缓冲区,速度要快许多。所以,在硬件条件允许的条件下要多申请硬件的缓冲区,特别是3-D缓冲区。
  缺省的情况下,DirectSound会优先申请硬件缓冲区,但是能够申请多少硬件的缓冲区是由硬件设备的性能决定的。硬件在同一时刻能播放的声音数量越多,可申请的硬件缓冲区的数量就越多,当创建一个缓冲区时,DirectSound就分配一个hardware voice,缓冲区销毁时就释放hardware voice。如果一个应用程序创建了很多的缓冲区,但是,很多缓冲区是由软件来销毁的,也就是说,这些缓冲区是由CPU而不是声卡来控制和混音的。
  注意:DirectSound 声音管理器分配的是硬件混音资源,并不是缓冲区,在一个PCI板卡上,缓冲区在分配前后都占用相同大小的内存,不管是将该缓冲区分配给硬件还是软件混音器。
  动态的声音管理,通过在缓冲区播放才进行voice  allocation,获取提前结束那么权限比较低的音频数据播放,释放他们的资源,从而可以减轻硬件设备的压力。
  当缓冲区正在play的时候,为了延迟硬件资源用来分配给混音,3-D特技,可以在创建缓冲区对象时,将DSBUFFERDESC结构的dwFlages设置为DSBCAPS_LOCDEFER标志,传递给IDirectSound8::CreateSoundBuffer.函数,当你对这样的缓冲区进行IDirectSoundBuffer8::Play or IDirectSoundBuffer8::AcquireResources操作时,DrectSound会尽可能的在硬件上play。
  当调用Play的时候,你可以试图通过传递下面表格中的参数将其他正在使用的硬件 voice资源释放掉。从而供你的buffer使用
  DSBPLAY_TERMINATEBY_TIME
  Select the buffer that has been playing longer than any other candidate buffers.
  DSBPLAY_TERMINATEBY_DISTANCE
  Select the 3-D candidate buffer farthest from the listener.
  DSBPLAY_TERMINATEBY_PRIORITY
  Select the buffer that has the lowest priority of candidate buffers, as set in the call to Play. If this is combined with one of the other two flags, the other flag is used only to resolve ties.
  DirectSound会根据你设置的标志,对所有正在play的buffer进行搜索,如果找到符合条件的缓冲区,DirectSound就会停掉该资源,然后将该资源分配给你的心的缓冲区使用。如果没有找到符合条件的缓冲区,那么你的新创建的缓冲区只好在软件缓冲区播放了,当然,如果你设置了DSBPLAY_LOCHARDWARE标志,此时,play调用就会失败。
  下面我们看看如何更有效地使用缓冲区。
  当使用流缓冲区的时候,要限制通知的次数和数据读写的次数。不要创建有很多通知positions的缓冲区,或者太小的缓冲区。流缓冲区在小于3个通知位置时工作的效率最高。当你改变一个辅助缓冲区的控制项时,此时效率就会受影响,所以,要尽可能少的调用IDirectSoundBuffer8::SetVolume, IDirectSoundBuffer8::SetFrequency.
  IDirectSoundBuffer8::SetPan, 函数,例如,你有个习惯总是喜欢在控制面板上来回的拖动左右声道的位置。
  一定要记住,3D缓冲区是需要占用的更多的CPU的。所以,要注意下面的事情
  1将经常播放的sounds放到硬件缓冲区中,
  2 Don't create 3-D buffers for sounds that won't benefit from the effect.
  3通过IDirectSound3DBuffer8::SetMode函数,设置DS3DMODE_DISABLE标志来停止对3d缓冲区的3d处理,
  4最好批量的参数进行调整。
  4.5向主缓冲区写数据(Writing to the Primary Buffer)
  当应用程序需要一些特殊的混音或者特技,而辅助缓冲区不支持这些功能,那么DirectSOund允许直接曹操主缓冲区,
  当你获得操作主缓冲区的权限时,其他的DirectSound特性就变得不可用了,辅助缓冲区没法混音,硬件加速混音器也无法工作。
  大多数的应用程序应该使用辅助缓冲区避免直接操作主缓冲区,因为可以申请大块的辅助缓冲区,可以提高足够长的写入时间,从而避免了音频数据产生缝隙的危险。
  只有当主缓冲区硬件时你才能操作它,所以你可以通过IDirectSoundBuffer8::GetCaps函数来查询,该函数的参数结构dwFlages成员设置为DSBCAPS_LOCHARDWARE,如果你想锁定一个正在被软件枚举的主缓冲区,会失败的。
  你通过IDirectSound8::CreateSoundBuffer函数来创建主缓冲区,只要设置DSBCAPS_PRIMARYBUFFER标志即可。同时要保证你的协作度为DSSCL_WRITEPRIMARY。
  下面的代码演示了如何获取向主缓冲区写数据的权限
  BOOL AppCreateWritePrimaryBuffer(
  LPDIRECTSOUND8 lpDirectSound,
  LPDIRECTSOUNDBUFFER *lplpDsb,
  LPDWORD lpdwBufferSize,
  HWND hwnd)
  {
  DSBUFFERDESC dsbdesc;
  DSBCAPS dsbcaps;
  HRESULT hr;
  WAVEFORMATEX wf;
  // Set up wave format structure.
  memset(&wf, 0, sizeof(WAVEFORMATEX));
  wf.wFormatTag = WAVE_FORMAT_PCM;
  wf.nChannels = 2;
  wf.nSamplesPerSec = 22050;
  wf.nBlockAlign = 4;
  wf.nAvgBytesPerSec =
  wf.nSamplesPerSec * wf.nBlockAlign;
  wf.wBitsPerSample = 16;
  // Set up DSBUFFERDESC structure.
  memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
  dsbdesc.dwSize = sizeof(DSBUFFERDESC);
  dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
  // Buffer size is determined by sound hardware.
  dsbdesc.dwBufferBytes = 0;
  dsbdesc.lpwfxFormat = NULL; // Must be NULL for primary buffers.
  // Obtain write-primary cooperative level.
  hr = lpDirectSound->SetCooperativeLevel(hwnd, DSSCL_WRITEPRIMARY);
  if SUCCEEDED(hr)
  {
  // Try to create buffer.
  hr = lpDirectSound->CreateSoundBuffer(&dsbdesc,
  lplpDsb, NULL);
  if SUCCEEDED(hr)
  {
  // Set primary buffer to desired format.
  hr = (*lplpDsb)->SetFormat(&wf);
  if SUCCEEDED(hr)
  {
  // If you want to know the buffer size, call GetCaps.
  dsbcaps.dwSize = sizeof(DSBCAPS);
  (*lplpDsb)->GetCaps(&dsbcaps);
  *lpdwBufferSize = dsbcaps.dwBufferBytes;
  return TRUE;
  }
  }
  }
  // Failure.
  *lplpDsb = NULL;
  *lpdwBufferSize = 0;
  return FALSE;
  }
  下面的代码演示了应用程序如何混音的,其中CustomMixer函数是用来将几个音频流混音的函数 。AppMixIntoPrimaryBuffer 应该定时的被调用。
  BOOL AppMixIntoPrimaryBuffer(
  APPSTREAMINFO* lpAppStreamInfo,
  LPDIRECTSOUNDBUFFER lpDsbPrimary,
  DWORD dwDataBytes,
  DWORD dwOldPos,
  LPDWORD lpdwNewPos)
  {
  LPVOID lpvPtr1;
  DWORD dwBytes1;
  LPVOID lpvPtr2;
  DWORD dwBytes2;
  HRESULT hr;
  // Obtain write pointer.
  hr = lpDsbPrimary->Lock(dwOldPos, dwDataBytes,
  &lpvPtr1, &dwBytes1,
  &lpvPtr2, &dwBytes2, 0);
  // If DSERR_BUFFERLOST is returned, restore and retry lock.
  if (DSERR_BUFFERLOST == hr)
  {
  lpDsbPrimary->Restore();
  hr = lpDsbPrimary->Lock(dwOldPos, dwDataBytes,
  &lpvPtr1, &dwBytes1,
  &lpvPtr2, &dwBytes2, 0);
  }
  if SUCCEEDED(hr)
  {
  // Mix data into the returned pointers.
  CustomMixer(lpAppStreamInfo, lpvPtr1, dwBytes1);
  *lpdwNewPos = dwOldPos + dwBytes1;
  if (NULL != lpvPtr2)
  {
  CustomMixer(lpAppStreamInfo, lpvPtr2, dwBytes2);
  *lpdwNewPos = dwBytes2; // Because it wrapped around.
  }
  // Release the data back to DirectSound.
  hr = lpDsbPrimary->Unlock(lpvPtr1, dwBytes1,
  lpvPtr2, dwBytes2);
  if SUCCEEDED(hr)
  {
  return TRUE;
  }
  }
  // Lock or Unlock failed.
  return FALSE;
  }
分享到:
评论

相关推荐

    DirectSound开发指南

    DirectSound开发指南 一、DirectSound简介(Introduction to DirectSound) 二、DirectSound初体验初体验初体验初体验(Getting Started with DirectSound) 三、DirectSound实用开发技巧实用开发技巧实用开发技巧...

    directsound开发指南-智慧的鱼

    DirectSound 是微软多媒体技术DirectX 的组成部分,封装了大量音频处理 API 函数,它可以提供快速的混音、硬件加速功能,并且可以直接访问相关设备, 当然,最主要的是它提供的功能与现有的设备驱动程序保持兼容性。...

    DirectX.DirectSound

    DirectSound开发指南 附: Microsoft.DirectX.DirectSound.dll

    DirectSound_Develop_Manual.rar_directsound

    DIRECTSOUND 开发指南 供大家学习directsound时参考

    windows声音应用程序开发指南 PPT

    windows声音应用程序开发指南 电子课件 PPT 本书介绍了Windows环境下进行音频(声音)程序设计的原理和方法。全书共分5章。 其中,第0章介绍了进行音频处理编程的一些入门知识;第1章讲解了DirectX中DirectSound的...

    Visual C++ 6 .0 多媒体开发指南.pdf

    开发指南 李博轩 编著 清华大学出版社 目 录 第1章 Visual C++ 6 .0简介....................... 1 1 .1 Visual C++ 6 .0 概述....................... 1 1 .2 Visual C++ 6 .0 用户界面..................... 2 1 ...

    诺基亚Java用户界面API程序员指南

    诺基亚Java 用户界面 API 由两个全新包组成,其中包括四个类及两个接口: •package com.nokia.mid.ui •classes FullCanvas , DirectUtils, and DeviceControl •interface DirectGraphics &#...

    vc++ 开发实例源码包

    ATL开发指南源码 内部包含了atl控件的开发以及如何应用,演示了COM的包容与集合、自动化、事件和连接点、枚举器和集合以及线程管理等等。 ATL实现的CDHtmlDialog模板类v1.03 使用了六个类五个模块类演示了atl的调用...

    vc++ 应用源码包_1

    ATL开发指南源码 内部包含了atl控件的开发以及如何应用,演示了COM的包容与集合、自动化、事件和连接点、枚举器和集合以及线程管理等等。 ATL实现的CDHtmlDialog模板类v1.03 使用了六个类五个模块类演示了atl的调用...

    vc++ 应用源码包_2

    ATL开发指南源码 内部包含了atl控件的开发以及如何应用,演示了COM的包容与集合、自动化、事件和连接点、枚举器和集合以及线程管理等等。 ATL实现的CDHtmlDialog模板类v1.03 使用了六个类五个模块类演示了atl的调用...

    vc++ 应用源码包_6

    ATL开发指南源码 内部包含了atl控件的开发以及如何应用,演示了COM的包容与集合、自动化、事件和连接点、枚举器和集合以及线程管理等等。 ATL实现的CDHtmlDialog模板类v1.03 使用了六个类五个模块类演示了atl的调用...

    vc++ 应用源码包_5

    ATL开发指南源码 内部包含了atl控件的开发以及如何应用,演示了COM的包容与集合、自动化、事件和连接点、枚举器和集合以及线程管理等等。 ATL实现的CDHtmlDialog模板类v1.03 使用了六个类五个模块类演示了atl的调用...

    vc++ 应用源码包_3

    ATL开发指南源码 内部包含了atl控件的开发以及如何应用,演示了COM的包容与集合、自动化、事件和连接点、枚举器和集合以及线程管理等等。 ATL实现的CDHtmlDialog模板类v1.03 使用了六个类五个模块类演示了atl的调用...

    ease软件 4.0版的特点和应用

    99年年末,ada公司开始致力于ease4.0的开发,并于2002年八月正式发布ease4.0及相关的使用手册和指南,这是ease目前的最新版。本文主要介绍ease软件的基本结构,特点和使用,特别是ease4.0的相对于3.0版所作的改进。 ...

Global site tag (gtag.js) - Google Analytics