一個DirectSound的例子,即錄即放
--------------------------------------------------------------------------
#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 ();
}