該篇整理的原始來源爲http://blog.csdn.net/leixiaohua1020/article/details/40540147。很是感謝該博主的無私奉獻,寫了很多關於不一樣多媒體庫的博文。讓我這個小白學習到很多。如今將其整理是爲了收錄,以備本身查看。git
DirectSound是微軟所開發DirectX的組件之一,能夠在Windows 操做系統上錄音,而且記錄波形音效(waveform sound)。目前DirectSound 是一個成熟的API ,提供許多有用的功能,例如可以在較高的分辨率播放多聲道聲音。DirectSound3D(DS3D)最先是1993年與 DirectX 3 一塊兒發表的。DirectX 8之後的DirectSound和DirectSound3D的(DS3D)被合稱DirectX Audio。windows
DirectSound有如下幾種對象:數組
圖1.DirectSound對象緩存
使用DirectSound播放音頻通常狀況下須要以下步驟:函數
1.初始化oop
2.循環播放聲音學習
1.初始化ui
1)建立一個IDirectSound8接口的對象this
經過DirectSoundCreate8()方法能夠建立一個設備對象。這個對象一般表明缺省的播放設備。DirectSoundCreate8()函數原型以下。spa
1 HRESULT DirectSoundCreate8( 2 LPCGUID lpcGuidDevice, 3 LPDIRECTSOUND8 * ppDS8, 4 LPUNKNOWN pUnkOuter 5 )
參數的含義以下:
lpcGuidDevice:要建立的設備對象的GUID。能夠指定爲NULL,表明默認的播放設備。
ppDS8:返回的IDirectSound8對象的地址。
pUnkOuter:必須設爲NULL。
例如以下代碼便可建立一個IDirectSound8接口的對象
1 IDirectSound8 *m_pDS=NULL; 2 DirectSoundCreate8(NULL,&m_pDS,NULL);
2) 設置協做級
Windows 是一個多任務環境,同一時間有多個應用程序去訪問設備。經過使用協做級別,DirectSound能夠確保應用程序不會在別的設備使用時去訪問,每一個 DirectSound應用程序都有一個協做級別,這個級別決定着訪問硬件的權限。
在建立一個設備對象之後,必須經過用IDirectSound8的SetCooperativeLevel()設置協做權限,不然將聽不到聲音。SetCooperativeLevel()的原型以下
1 HRESULT SetCooperativeLevel( 2 HWND hwnd, 3 DWORD dwLevel 4 )
參數的含義以下:
hwnd:應用程序窗口句柄。
dwLevel:支持如下幾種級別:
DSSCL_EXCLUSIVE:與DSSCL_PRIORITY具備相同的做用。
DSSCL_NORMAL:正常的協調層級標誌,其餘程序可共享聲卡設備進行播放。
DSSCL_PRIORITY:設置聲卡設備爲當前程序獨佔。
DSSCL_WRITEPRIMAR:可寫主緩衝區,此時副緩衝區就不能進行播放處理,即不能將次緩衝區的數據送進混聲器,再輸出到主緩衝區上。這是最徹底控制聲音播放的方式。
3) 建立一個主緩衝對象
使用IDirectSound8的CreateSoundBuffer()能夠建立一個IDirectSoundBuffer接口的主緩衝區對象。CreateSoundBuffer()的原型以下。
1 HRESULT CreateSoundBuffer( 2 LPCDSBUFFERDESC pcDSBufferDesc, 3 LPDIRECTSOUNDBUFFER * ppDSBuffer, 4 LPUNKNOWN pUnkOuter 5 )
參數的含義以下:
pcDSBufferDesc:描述聲音緩衝的DSBUFFERDESC結構體的地址
ppDSBuffer:返回的IDirectSoundBuffer接口的對象的地址。
pUnkOuter:必須設置爲NULL。
其中涉及到一個描述聲音緩衝的結構體DSBUFFERDESC,該結構體的定義以下:
1 typedef struct _DSBUFFERDESC 2 { 3 DWORD dwSize; 4 DWORD dwFlags; 5 DWORD dwBufferBytes; 6 DWORD dwReserved; 7 LPWAVEFORMATEX lpwfxFormat; 8 } DSBUFFERDESC
簡單解釋一下其中的變量的含義:
dwSize:結構體的大小。必須初始化該值。
dwFlags:設置聲音緩存的屬性。有不少選項,能夠組合使用,就不一一列出了。詳細的參數能夠查看文檔。
dwBufferBytes:緩衝的大小。
dwReserved:保留參數,暫時沒有用。
lpwfxFormat:指向一個WAVE格式文件頭的指針。
設置DSBUFFERDESC完畢後,就可使用CreateSoundBuffer()建立主緩衝了。示例代碼以下:
1 DSBUFFERDESC dsbd; 2 memset(&dsbd,0,sizeof(dsbd)); 3 dsbd.dwSize=sizeof(dsbd); 4 dsbd.dwFlags=DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY |DSBCAPS_GETCURRENTPOSITION2; 5 dsbd.dwBufferBytes=MAX_AUDIO_BUF*BUFFERNOTIFYSIZE; 6 //WAVE Header 7 dsbd.lpwfxFormat=(WAVEFORMATEX*)malloc(sizeof(WAVEFORMATEX)); 8 dsbd.lpwfxFormat->wFormatTag=WAVE_FORMAT_PCM; 9 /* format type */ 10 (dsbd.lpwfxFormat)->nChannels=channels; 11 /* number of channels (i.e. mono, stereo...) */ 12 (dsbd.lpwfxFormat)->nSamplesPerSec=sample_rate; 13 /* sample rate */ 14 (dsbd.lpwfxFormat)->nAvgBytesPerSec=sample_rate*(bits_per_sample/8)*channels; 15 /* for buffer estimation */ 16 (dsbd.lpwfxFormat)->nBlockAlign=(bits_per_sample/8)*channels; 17 /* block size of data */ 18 (dsbd.lpwfxFormat)->wBitsPerSample=bits_per_sample; 19 /* number of bits per sample of mono data */ 20 (dsbd.lpwfxFormat)->cbSize=0; 21 22 23 //Creates a sound buffer object to manage audio samples. 24 HRESULT hr1; 25 if( FAILED(m_pDS->CreateSoundBuffer(&dsbd,&m_pDSBuffer,NULL))){ 26 return FALSE; 27 }
4) 建立一個副緩衝對象
使用IDirectSoundBuffer的QueryInterface()能夠獲得一個IDirectSoundBuffer8接口的對象。IDirectSoundBuffer8的GUID爲IID_IDirectSoundBuffer8。示例代碼以下。
1 IDirectSoundBuffer *m_pDSBuffer=NULL; 2 IDirectSoundBuffer8 *m_pDSBuffer8=NULL; 3 ... 4 if( FAILED(m_pDSBuffer->QueryInterface(IID_IDirectSoundBuffer8,(LPVOID*)&m_pDSBuffer8))){ 5 return FALSE ; 6 }
5) 建立通知對象
使用IDirectSoundBuffer8的QueryInterface()能夠獲得一個IDirectSoundNotify8接口的對象。IDirectSoundBuffer8的GUID爲IID_IDirectSoundNotify。示例代碼以下。
1 IDirectSoundBuffer8 *m_pDSBuffer8=NULL; 2 IDirectSoundNotify8 *m_pDSNotify=NULL; 3 … 4 if(FAILED(m_pDSBuffer8->QueryInterface(IID_IDirectSoundNotify,(LPVOID*)&m_pDSNotify))){ 5 return FALSE ; 6 }
一句話歸納一下通知對象的做用:當DirectSound緩衝區中的數據播放完畢後,告知系統應該填充新的數據。
6) 設置通知位置
使用IDirectSoundNotify8的SetNotificationPositions()能夠設置通知的位置。SetNotificationPositions()的原型以下。
1 HRESULT SetNotificationPositions( 2 DWORD dwPositionNotifies, 3 LPCDSBPOSITIONNOTIFY pcPositionNotifies 4 )
參數含義以下。
dwPositionNotifies:DSBPOSITIONNOTIFY結構體的數量。既包含幾個通知的位置。
pcPositionNotifies:指向DSBPOSITIONNOTIFY結構體數組的指針。
在這裏涉及到一個結構體DSBPOSITIONNOTIFY,它描述了通知的位置。DSBPOSITIONNOTIFY的定義以下。
1 typedef struct DSBPOSITIONNOTIFY { 2 DWORD dwOffset; 3 HANDLE hEventNotify; 4 } DSBPOSITIONNOTIFY;
它的成員的含義以下。
dwOffset:通知事件觸發的位置(距離緩衝開始位置的偏移量)。
hEventNotify:觸發的事件的句柄。
7) 開始播放
使用IDirectSoundBuffer8的SetCurrentPosition ()能夠設置播放的位置。SetCurrentPosition ()原型以下
1 HRESULT SetCurrentPosition( 2 DWORD dwNewPosition 3 )
其中dwNewPosition是播放點與緩衝區首個字節之間的偏移量。
使用IDirectSoundBuffer8的Play ()能夠開始播放音頻數據。Play ()原型以下。
1 HRESULT Play( 2 DWORD dwReserved1, 3 DWORD dwPriority, 4 DWORD dwFlags 5 )
參數含義:
dwReserved1:保留參數,必須取0。
dwPriority:優先級,通常狀況下取0便可。
dwFlags:標誌位。目前常見的是DSBPLAY_LOOPING。當播放至緩衝區結尾的時候,從新從緩衝區開始處開始播放。
2. 循環播放聲音
1) 數據填充至副緩衝區
數據填充至副緩衝區以前,須要先使用Lock()鎖定緩衝區。而後就可使用fread(),memcpy()等方法將PCM音頻採樣數據填充至緩衝區。數據填充完畢後,使用Unlock()取消對緩衝區的鎖定。若是是實時採集的音頻數據,只要將音頻數據複製到Lock()獲取到的ppvAudioPtr1指向的地址,大小爲pdwAudioBytes1,就能夠播放了。(我使用的方式就是如此,實現了實時音頻的播放,下文中的例子數據是讀取自文件。)
Lock()函數的原型以下。
1 HRESULT Lock( 2 DWORD dwOffset, 3 DWORD dwBytes, 4 LPVOID * ppvAudioPtr1, 5 LPDWORD pdwAudioBytes1, 6 LPVOID * ppvAudioPtr2, 7 LPDWORD pdwAudioBytes2, 8 DWORD dwFlags 9 )
參數的含義以下。
dwOffset:鎖定的內存與緩衝區首地址之間的偏移量。
dwBytes:鎖定的緩存的大小。
ppvAudioPtr1:獲取到的指向緩存數據的指針。
pdwAudioBytes1:獲取到的緩存數據的大小。
ppvAudioPtr2:沒有用到,設置爲NULL。
pdwAudioBytes2:沒有用到,設置爲0。
dwFlags:暫時沒有研究。
UnLock()函數的原型以下。
1 HRESULT Unlock( 2 LPVOID pvAudioPtr1, 3 DWORD dwAudioBytes1, 4 LPVOID pvAudioPtr2, 5 DWORD dwAudioBytes2 6 )
參數含義以下。
pvAudioPtr1:經過Lock()獲取到的指向緩存數據的指針。
dwAudioBytes1:寫入的數據量。
pvAudioPtr2:沒有用到。
dwAudioBytes2:沒有用到。
2) 等待播放完成
根據此前設置的通知機制,使用WaitForMultipleObjects()等待緩衝區中的數據播放完畢,而後進入下一個循環。
DirectSound播放PCM音頻數據的流程以下圖所示。
圖2
其中涉及到的幾個結構體之間的關係以下圖所示。
圖3.結構體關係
該代碼也是直接使用的來自原博主的代碼,以下
1 /** 2 * 最簡單的DirectSound播放音頻的例子(DirectSound播放PCM) 3 * Simplest Audio Play DirectSound (DirectSound play PCM) 4 * 5 * 雷霄驊 Lei Xiaohua 6 * leixiaohua1020@126.com 7 * 中國傳媒大學/數字電視技術 8 * Communication University of China / Digital TV Technology 9 * http://blog.csdn.net/leixiaohua1020 10 * 11 * 本程序使用DirectSound播放PCM音頻採樣數據。 12 * 是最簡單的DirectSound播放音頻的教程。 13 * 14 * 函數調用步驟以下: 15 * 16 * [初始化] 17 * DirectSoundCreate8():建立一個DirectSound對象。 18 * SetCooperativeLevel():設置協做權限,否則沒有聲音。 19 * IDirectSound8->CreateSoundBuffer():建立一個主緩衝區對象。 20 * IDirectSoundBuffer->QueryInterface(IID_IDirectSoundBuffer8..): 21 * 建立一個副緩衝區對象,用來存儲要播放的聲音數據文件。 22 * IDirectSoundBuffer8->QueryInterface(IID_IDirectSoundNotify..): 23 * 建立通知對象,通知應用程序指定播放位置已經達到。 24 * IDirectSoundNotify8->SetNotificationPositions():設置通知位置。 25 * IDirectSoundBuffer8->SetCurrentPosition():設置播放的起始點。 26 * IDirectSoundBuffer8->Play():開始播放。 27 * 28 * [循環播放數據] 29 * IDirectSoundBuffer8->Lock():鎖定副緩衝區,準備寫入數據。 30 * fread():讀取數據。 31 * IDirectSoundBuffer8->Unlock():解鎖副緩衝區。 32 * WaitForMultipleObjects():等待「播放位置已經達到」的通知。 33 * 34 * This software plays PCM raw audio data using DirectSound. 35 * It's the simplest tutorial about DirectSound. 36 * 37 * The process is shown as follows: 38 * 39 * [Init] 40 * DirectSoundCreate8(): Init DirectSound object. 41 * SetCooperativeLevel(): Must set, or we won't hear sound. 42 * IDirectSound8->CreateSoundBuffer(): Create primary sound buffer. 43 * IDirectSoundBuffer->QueryInterface(IID_IDirectSoundBuffer8..): 44 * Create secondary sound buffer. 45 * IDirectSoundBuffer8->QueryInterface(IID_IDirectSoundNotify..): 46 * Create Notification object. 47 * IDirectSoundNotify8->SetNotificationPositions(): 48 * Set Notification Positions. 49 * IDirectSoundBuffer8->SetCurrentPosition(): Set position to start. 50 * IDirectSoundBuffer8->Play(): Begin to play. 51 * 52 * [Loop to play data] 53 * IDirectSoundBuffer8->Lock(): Lock secondary buffer. 54 * fread(): get PCM data. 55 * IDirectSoundBuffer8->Unlock(): UnLock secondary buffer. 56 * WaitForMultipleObjects(): Wait for Notifications. 57 */ 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <windows.h> 61 #include <dsound.h> 62 63 64 #define MAX_AUDIO_BUF 4 65 #define BUFFERNOTIFYSIZE 192000 66 67 68 int sample_rate=8000; //PCM sample rate 69 int channels=1; //PCM channel number 70 int bits_per_sample=16; //bits per sample 71 72 BOOL main(int argc,char * argv[]) 73 { 74 int i; 75 FILE * fp; 76 if((fp=fopen("../out.pcm","rb"))==NULL){ 77 printf("cannot open this file\n"); 78 return -1; 79 } 80 81 IDirectSound8 *m_pDS=0; 82 IDirectSoundBuffer8 *m_pDSBuffer8=NULL; //used to manage sound buffers. 83 IDirectSoundBuffer *m_pDSBuffer=NULL; 84 IDirectSoundNotify8 *m_pDSNotify=0; 85 DSBPOSITIONNOTIFY m_pDSPosNotify[MAX_AUDIO_BUF]; 86 HANDLE m_event[MAX_AUDIO_BUF]; 87 88 SetConsoleTitle(TEXT("Simplest Audio Play DirectSound"));//Console Title 89 //Init DirectSound 90 if(FAILED(DirectSoundCreate8(NULL,&m_pDS,NULL))) 91 return FALSE; 92 if(FAILED(m_pDS->SetCooperativeLevel(FindWindow(NULL,TEXT("Simplest Audio Play DirectSound")),DSSCL_NORMAL))) 93 return FALSE; 94 95 96 DSBUFFERDESC dsbd; 97 memset(&dsbd,0,sizeof(dsbd)); 98 dsbd.dwSize=sizeof(dsbd); 99 dsbd.dwFlags=DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY |DSBCAPS_GETCURRENTPOSITION2; 100 dsbd.dwBufferBytes=MAX_AUDIO_BUF*BUFFERNOTIFYSIZE; 101 dsbd.lpwfxFormat=(WAVEFORMATEX*)malloc(sizeof(WAVEFORMATEX)); 102 dsbd.lpwfxFormat->wFormatTag=WAVE_FORMAT_PCM; 103 /* format type */ 104 (dsbd.lpwfxFormat)->nChannels=channels; 105 /* number of channels (i.e. mono, stereo...) */ 106 (dsbd.lpwfxFormat)->nSamplesPerSec=sample_rate; 107 /* sample rate */ 108 (dsbd.lpwfxFormat)->nAvgBytesPerSec=sample_rate*(bits_per_sample/8)*channels; 109 /* for buffer estimation */ 110 (dsbd.lpwfxFormat)->nBlockAlign=(bits_per_sample/8)*channels; 111 /* block size of data */ 112 (dsbd.lpwfxFormat)->wBitsPerSample=bits_per_sample; 113 /* number of bits per sample of mono data */ 114 (dsbd.lpwfxFormat)->cbSize=0; 115 116 //Creates a sound buffer object to manage audio samples. 117 HRESULT hr1; 118 if( FAILED(m_pDS->CreateSoundBuffer(&dsbd,&m_pDSBuffer,NULL))){ 119 return FALSE; 120 } 121 if( FAILED(m_pDSBuffer->QueryInterface(IID_IDirectSoundBuffer8,(LPVOID*)&m_pDSBuffer8))){ 122 return FALSE ; 123 } 124 //Get IDirectSoundNotify8 125 if(FAILED(m_pDSBuffer8->QueryInterface(IID_IDirectSoundNotify,(LPVOID*)&m_pDSNotify))){ 126 return FALSE ; 127 } 128 for(i =0;i<MAX_AUDIO_BUF;i++){ 129 m_pDSPosNotify[i].dwOffset =i*BUFFERNOTIFYSIZE; 130 m_event[i]=::CreateEvent(NULL,false,false,NULL); 131 m_pDSPosNotify[i].hEventNotify=m_event[i]; 132 } 133 m_pDSNotify->SetNotificationPositions(MAX_AUDIO_BUF,m_pDSPosNotify); 134 m_pDSNotify->Release(); 135 136 //Start Playing 137 BOOL isPlaying =TRUE; 138 LPVOID buf=NULL; 139 DWORD buf_len=0; 140 DWORD res=WAIT_OBJECT_0; 141 DWORD offset=BUFFERNOTIFYSIZE; 142 143 m_pDSBuffer8->SetCurrentPosition(0); 144 m_pDSBuffer8->Play(0,0,DSBPLAY_LOOPING); 145 //Loop 146 while(isPlaying){ 147 if((res >=WAIT_OBJECT_0)&&(res <=WAIT_OBJECT_0+3)){ 148 m_pDSBuffer8->Lock(offset,BUFFERNOTIFYSIZE,&buf,&buf_len,NULL,NULL,0); 149 150 // 若是是實時音頻播放,那麼下面的數據就能夠把內存中buf_len大小的數據複製到buf指向的地址便可 151 if(fread(buf,1,buf_len,fp)!=buf_len){ 152 //File End 153 //Loop: 154 fseek(fp, 0, SEEK_SET); 155 fread(buf,1,buf_len,fp); 156 //Close: 157 //isPlaying=0; 158 } 159 160 offset+=buf_len; 161 offset %= (BUFFERNOTIFYSIZE * MAX_AUDIO_BUF); 162 printf("this is %7d of buffer\n",offset); 163 m_pDSBuffer8->Unlock(buf,buf_len,NULL,0); 164 } 165 res = WaitForMultipleObjects (MAX_AUDIO_BUF, m_event, FALSE, INFINITE); 166 } 167 168 return 0; 169 }
最後,再次強調下,該博文是整理自http://blog.csdn.net/leixiaohua1020/article/details/40540147。我只是改變了一點點格式,其實改變的地方很是少。只是加了點註釋,即播放實時內存數據怎麼使用(這是我在項目中的使用方式)。我一再強調,是爲了尊重原博主的工做,畢竟直接把別人的東西拿來看成本身的,那就是小偷了。