现在的位置: 首页 > 综合 > 正文

一个DirectSound的例子,即录即放

2018年02月08日 ⁄ 综合 ⁄ 共 13890字 ⁄ 字号 评论关闭

一个捕获音频并且播放的例子,可以用来唱歌^_^

写了半天才发现Direct SDK有个类似的例子,所以到了最后几乎都是照抄了。

声音效果不太好,修改一下加上网络传送功能做成语音聊天工具。不过这样肯定不行,真正的语音聊天可能都要音频数据处理啊等功能的。

我是想试这做个东西来唱歌,不过效果不好!程序错误很多,有很多地方可以改进的,不过我一贯的作风是浅尝即止。

 

部分代码
--------------------------------------------------------------------------
#include "sound.h"
LPDIRECTSOUND8 lpDirectSound =NULL;       //DirectSound 设备
LPDIRECTSOUNDBUFFER8 lpDSBuffer8 = NULL; //播放缓冲区(第二缓冲区)
HANDLE soundEvent[3];    //播放通知
LPDIRECTSOUNDCAPTURE8        lpDSCapture = NULL;//捕获设备对象指针
LPDIRECTSOUNDCAPTUREBUFFER8   lpDSBCapture = NULL;//捕获缓冲区对象指针
HANDLE captureEvent[3];    //播放通知
//int   FreeBufferSectionNum =0;   //第几段的 播放缓冲可以写了
int   SoundBufferLength=0;     //播放缓冲的总长度, 一个缓冲设置分成3段,设置3个通知信号
int   BufferSectionLength=0; //每一段的长度
BYTE   SwapBuffer[29400] ;//应该是SwapBuffer[BufferSectionLength];   ,
                                       //不过我懒的动态申请了,所以固定了,用于把临时保存捕获的音频数据,
                                        //在从这里复制到播放缓存里面去。 其实直接复制也是可以的,不过加多一个缓存在这里,便于处理啊,网络传送等等。
                          
//CWaveFile   waveFile= NULL;  
bool SoundCreate(HWND hwnd )
{
     HRESULT hr = DirectSoundCreate8(NULL, & lpDirectSound, NULL);
if (FAILED(hr))
     {    
           MessageBox(hwnd,_T("创建DirectSound接口失败。"),_T("widebright"),MB_OK);    // Add error-handling here.
        return false;
     }
     hr = lpDirectSound->SetCooperativeLevel(hwnd, DSSCL_PRIORITY   ); //DSSCL_NORMAL
     if (FAILED(hr))
     {    
     MessageBox(hwnd,_T("设置DirectSound协作级别失败。"),_T("widebright"),MB_OK);    // Add error-handling here.
           lpDirectSound->Release ();    // Add error-handling here.
        return false;
     }
    
hr =   CreateBasicBuffer(lpDirectSound ,& lpDSBuffer8);
     if (FAILED(hr))
     {    
     MessageBox(hwnd,_T("创建DirectSound声音播放缓冲区失败。"),_T("widebright"),MB_OK);    // Add error-handling here.
           lpDirectSound->Release ();    // Add error-handling here.
        return false;
     }
//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);
   return true;
}
//创建 播放缓冲
//
//The example function creates a streaming buffer large enough to hold 3 seconds of streaming data. Nonstreaming buffers should be made just large enough to accommodate the entire sound.
//
//The DSBCAPS_GLOBALFOCUS flag in the example ensures that the buffer will continue playing even when the application window is not in the foreground. Without this flag, the buffer will be muted when another application or even a dialog box has the input focus.
//
//If the location of a buffer is not specified, DirectSound places it in hardware-controlled memory if possible. Because hardware buffers are mixed by the sound card processor, they have much less impact on application performance.
//
//If you wish to specify the location of a buffer rather than letting DirectSound decide where it belongs, set either the DSBCAPS_LOCHARDWARE or DSBCAPS_LOCSOFTWARE flag in the DSBUFFERDESC structure. If the DSBCAPS_LOCHARDWARE flag is set and there are insufficient hardware resources, the buffer creation request fails.
//
//To take advantage of the voice management features of DirectSound, specify the DSBCAPS_LOCDEFER flag when creating the buffer. This flag defers the allocation of resources for the buffer until it is played. For more information, see Dynamic Voice Management.
//
//You can ascertain the location of an existing buffer by using the IDirectSoundBuffer8::GetCaps method and checking the dwFlags member of the DSBCAPS structure for either the DSBCAPS_LOCHARDWARE or DSBCAPS_LOCSOFTWARE flags. One or the other is always specified.
//
//Buffer objects are owned by the device object that created them. When the device object is released, all buffers created by that object are also released and should not be referenced.
HRESULT CreateBasicBuffer(LPDIRECTSOUND8 lpDirectSound, LPDIRECTSOUNDBUFFER8* ppDsb8)
{
   WAVEFORMATEX wfx;
   DSBUFFERDESC dsbdesc;
   LPDIRECTSOUNDBUFFER pDsb = NULL;
   HRESULT hr;

   // Set up WAV format structure.

   memset(&wfx, 0, sizeof(WAVEFORMATEX));
   wfx.wFormatTag = WAVE_FORMAT_PCM;
   wfx.nChannels = 2;
   wfx.nSamplesPerSec = 22050;
   wfx.nBlockAlign = 4;
   wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
   wfx.wBitsPerSample = 16;

   // Set up DSBUFFERDESC structure.

   memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
   dsbdesc.dwSize = sizeof(DSBUFFERDESC);
   dsbdesc.dwFlags =
      DSBCAPS_CTRLPAN      //声源 是否可以左右移动
//DSBCAPS_CTRL3D      //声源是否可以在   3D空移动。
//DSBCAPS_CTRLFX      //特效处理支持
| DSBCAPS_CTRLVOLUME           //音量控制
//| DSBCAPS_CTRLFREQUENCY     //频率控制
| DSBCAPS_CTRLPOSITIONNOTIFY //可以设置播放位置通知
     | DSBCAPS_GLOBALFOCUS;   //在程序失去焦点的时候,依然播放声音
   dsbdesc.dwBufferBytes =   wfx.nAvgBytesPerSec/2;       //设置缓冲区长度 为多少秒,就设置这里为wfx.nAvgBytesPerSec 乘以多少,这里和捕获的统一设置成一秒
   dsbdesc.lpwfxFormat = &wfx;

SoundBufferLength=dsbdesc.dwBufferBytes ; //记录缓冲的总长度**************************************************
BufferSectionLength = SoundBufferLength/3   ; //平均分成3段,每段的长度
BufferSectionLength -=   BufferSectionLength % wfx.nBlockAlign; //内存对齐
   // Create buffer.

   hr = lpDirectSound->CreateSoundBuffer(&dsbdesc, &pDsb, NULL);
   if (SUCCEEDED(hr))
   {
      hr = pDsb->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*) ppDsb8);
      pDsb->Release();
   }
   return hr;
}

//设置通知对象
//The buffer must be stopped when this method is called.
//dwOffset---Offset from the beginning of the buffer where the notify event is to be triggered, or DSBPN_OFFSETSTOP. 通知触发时buffer的偏移
// hEventNotify ---Handle to the event to be signaled when the offset has been reached.   通知触发的event
HRESULT SetNotification(HANDLE   *hEventNotify ,LPDIRECTSOUNDBUFFER8 lpDsbSecondary )
{
   #define cEvents   3

   LPDIRECTSOUNDNOTIFY8 lpDsNotify;
   DSBPOSITIONNOTIFY PositionNotify[cEvents];
   HRESULT hr;
  
   if (FAILED( hr = lpDsbSecondary->QueryInterface(IID_IDirectSoundNotify8,
              (LPVOID*)&lpDsNotify)))
   {
     return hr;
   }

   // Create events.
   for (int i = 0; i < cEvents; ++i)
   {
     hEventNotify[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
     if (NULL == hEventNotify[i])
     {
       hr = GetLastError();
       return hr;
     }
      PositionNotify[i].dwOffset =   BufferSectionLength   * (i+1)   -1;   ;    //设置这里值 为DSBPN_OFFSETSTOP 时,可以创建一个停止播放通知
      PositionNotify[i].hEventNotify = hEventNotify[i];
   
   }
  
     
   //注意没调用一次SetNotificationPositions函数就清除以前的通知对象,所以要设置多个通知对象只能使用一个PositionNotify数组,而不能调用多次SetNotificationPositions函数
     hr = lpDsNotify->SetNotificationPositions(3, PositionNotify);   
     lpDsNotify->Release();
     return hr;
}
BOOL AppWriteDataToBuffer(
         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)   //如果lpvPtr2不为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;
     }
///////////////////////////////////
// 启动 播放声音
bool   SoundStart(HWND hwnd )
{
HRESULT hr;
      if( ! SoundCreate(hwnd)) exit(0);
    

//不采用 播放通知了,播放通知控制起来不方便,只需要的捕获通知里面复制 到播放缓存的相应位置好了
//    
//   HRESULT   hr = SetNotification(soundEvent,lpDSBuffer8);
//   if (FAILED(hr))
//    {    
//    MessageBox(hwnd,_T("创建播放位置通知失败"),_T("widebright"),MB_OK);  
//    exit(0);
//}

ZeroMemory( SwapBuffer,29400);   //无声

AppWriteDataToBuffer (lpDSBuffer8,0,SwapBuffer,29400);
lpDSBuffer8->SetVolume(DSBVOLUME_MAX);
lpDSBuffer8->SetCurrentPosition (0);
if (SUCCEEDED( hr = lpDSBuffer8->Play (0,0,DSBPLAY_LOOPING))) //开始播放
{
     return true;
}else{
     return false;
}
//不采用 播放通知了,播放通知控制起来不方便,只需要的捕获通知里面复制 到播放缓存的相应位置好了
//while (1)
//   {
// //       DWORD   notifyIndex =   WaitForMultipleObjects(3,soundEvent,FALSE,INFINITE) ;     //INFINITE 表示一直等待 ,除非hEvent产生了信号。 这里
// //      
// //notifyIndex = notifyIndex - WAIT_OBJECT_0;    //得到触发 通知对象序号
// //       ResetEvent(soundEvent[ notifyIndex]);
//               
//      //FreeBufferSectionNum = notifyIndex;
//    WaitForSingleObject(soundEvent[0], INFINITE);       
//    ResetEvent(soundEvent[ 0]);
//       FreeBufferSectionNum =0;
//    WaitForSingleObject(soundEvent[1], INFINITE);       
//    ResetEvent(soundEvent[1]);
//    FreeBufferSectionNum =1;
//    WaitForSingleObject(soundEvent[2], INFINITE);       
//    ResetEvent(soundEvent[ 2]);
//       FreeBufferSectionNum =2;
//
//}  
}
////////////////////
//停止播放声音
void SoundStop()
{
if ( ! lpDSBuffer8)   {lpDSBuffer8->Stop(); lpDSBuffer8->Release ();}
     if ( ! lpDirectSound) lpDirectSound->Release ();
    
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////下面是录音部分/////////////////////////////////////////////////////////////////////
bool CaptureCreate(HWND hwnd )
{
                 
     HRESULT hr = DirectSoundCaptureCreate8 (&DSDEVID_DefaultCapture , &lpDSCapture,NULL);
if (FAILED(hr))
     {    
           MessageBox(hwnd,_T("创建DirectCapture接口失败。"),_T("widebright"),MB_OK);    // Add error-handling here.
        return false;
     }
  
hr =   CreateCaptureBuffer(lpDSCapture , &lpDSBCapture);
     if (FAILED(hr))
     {    
     MessageBox(hwnd,_T("创建DirectCapture声音播放缓冲区失败。"),_T("widebright"),MB_OK);    // Add error-handling here.
           lpDSCapture->Release ();    // Add error-handling here.
        return false;
     }
//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);
   return true;
}
HRESULT CreateCaptureBuffer(LPDIRECTSOUNDCAPTURE8 pDSC,
                             LPDIRECTSOUNDCAPTUREBUFFER8* ppDSCB8)
{
   HRESULT hr;
   DSCBUFFERDESC                dscbd;
   LPDIRECTSOUNDCAPTUREBUFFER   pDSCB;
   WAVEFORMATEX                 wfx =
      {WAVE_FORMAT_PCM, 2, 22050, 88200, 4, 16, 0};
     // {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/2;      //*******保存多少秒的数据 就设置这里为多少乘以 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;
}

HRESULT SetCaptureNotifications(HANDLE   *rghEvent, 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 < cEvents; ++i)
   {
     rghEvent[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
     if (NULL == rghEvent[i])
     {
       hr = GetLastError();
       return hr;
     }
       // Describe notifications.
         rgdsbpn[i].dwOffset =   BufferSectionLength   * (i+1)   -1;
         rgdsbpn[i].hEventNotify = rghEvent[0];    //rghEvent[i];   可以多个通知对象共用一个event的
   }
   // Create notifications.

   hr = pDSNotify->SetNotificationPositions(cEvents, rgdsbpn);
   pDSNotify->Release();
   return hr;
}

///////////////////////////////////
// 启动 播放声音
bool   CaptureStart(HWND hwnd )
{
     if( ! CaptureCreate(hwnd)) exit(0);
    
  
   HRESULT   hr = SetCaptureNotifications( captureEvent,lpDSBCapture );
      if (FAILED(hr))
   {    
     MessageBox(hwnd,_T("创建捕获位置通知失败"),_T("widebright"),MB_OK);  
        exit(0);
   }
  
    
lpDSBCapture->Start (DSCBSTART_LOOPING); //开始捕获
    int NextCaptureOffset =0;   //捕获缓存区 偏移
     DWORD playCursor=0,writeCursor=0;
   int NextSoundOffset = 0;   //播放缓存区 的偏移,
     lpDSBuffer8->GetCurrentPosition(&playCursor,&writeCursor);
     NextSoundOffset = (writeCursor + 600 )% SoundBufferLength;
while (1)
    {
     //    DWORD   notifyIndex =   WaitForMultipleObjects(3,captureEvent,FALSE,INFINITE) ;     //INFINITE 表示一直等待 ,除非hEvent产生了信号。 这里
     //    
     //notifyIndex = notifyIndex - WAIT_OBJECT_0;    //得到触发 通知对象序号
     //    ResetEvent(captureEvent[ notifyIndex]);
      VOID* pDSCaptureLockedBuffer     = NULL;
      DWORD dwDSCaptureLockedBufferSize;
     WaitForSingleObject(captureEvent[0], INFINITE);       
     ResetEvent(captureEvent[ 0]);
         if( SUCCEEDED( hr = lpDSBCapture->Lock(NextCaptureOffset, BufferSectionLength,
                                           &pDSCaptureLockedBuffer,
                                           &dwDSCaptureLockedBufferSize,
                                           NULL, NULL, 0L ) ) )
   {
        CopyMemory( SwapBuffer,
                 pDSCaptureLockedBuffer,
                 dwDSCaptureLockedBufferSize );
            
    lpDSBCapture->Unlock( pDSCaptureLockedBuffer, dwDSCaptureLockedBufferSize,
                            NULL, 0 );
           
            lpDSBuffer8->GetCurrentPosition(&playCursor,&writeCursor);
//     AppWriteDataToBuffer (lpDSBuffer8,writeCursor   ,SwapBuffer,dwDSCaptureLockedBufferSize);
   AppWriteDataToBuffer (lpDSBuffer8, NextSoundOffset ,SwapBuffer,dwDSCaptureLockedBufferSize);

   }
        NextSoundOffset +=dwDSCaptureLockedBufferSize;
        NextSoundOffset %= SoundBufferLength; // Circular buffer
        NextCaptureOffset +=   BufferSectionLength;
        NextCaptureOffset %= SoundBufferLength; // Circular buffer
    
       
}

}
////////////////////
//停止播放声音
void CaptureStop()
{
if ( ! lpDSBCapture)   {lpDSBuffer8->Stop (); lpDSBCapture->Release ();}
     if ( ! lpDSCapture ) lpDSCapture ->Release ();
    
更多技术文章请参看施昌权的个人网站: http://www.joyvc.cn

抱歉!评论已关闭.