多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區(qū)平臺 聯系我們 | 收藏本站
阿里云優(yōu)惠2
您當前位置:首頁 > 互聯網 > 最簡單的視音頻播放示例8:DirectSound播放PCM

最簡單的視音頻播放示例8:DirectSound播放PCM

來源:程序員人生   發(fā)布時間:2014-11-04 08:54:08 閱讀次數:3566次
本文記錄DirectSound播放音頻的技術。DirectSound是Windows下最多見的音頻播放技術。目前大部份的音頻播放利用都是通過DirectSound來播放的。本文記錄1個使用DirectSound播放PCM的例子。
注:1位仁兄已提示我DirectSound已計劃被XAudio2取代了。后來考證了1下發(fā)現確有此事。因此在下次更新中斟酌加入XAudio2播放PCM的例子。本文依然記錄1下DirectSound這位“元老”。



DirectSound簡介

DirectSound是微軟所開發(fā)DirectX的組件之1,可以在Windows 操作系統(tǒng)上錄音,并且記錄波形音效(waveform sound)。目前DirectSound 是1個成熟的API ,提供許多有用的功能,例如能夠在較高的分辨率播放多聲道聲音。
DirectSound3D(DS3D)最早是1993年與 DirectX 3 1起發(fā)表的。DirectX 8以后的DirectSound和DirectSound3D的(DS3D)被合稱DirectX Audio。


DirectSound有以下幾種對象:

對象

數量

作用

主要接口

裝備

每一個利用程序只有1個裝備對象

用來管理裝備,創(chuàng)建輔助緩沖區(qū)

IDirectSound8

輔助緩沖區(qū)

每個聲音對應1個輔助緩沖區(qū)

用來管理1個靜態(tài)的或動態(tài)的聲音流,然后在主緩沖區(qū)中混音

IDirectSoundBuffer8,

IDirectSound3DBuffer8,

IDirectSoundNotify8

主緩沖區(qū)

1個利用程序只有1個主緩沖區(qū)

將輔助緩沖區(qū)的數據進行混音,并且控制3D參數.

IDirectSoundBuffer,

IDirectSound3DListener8



DirectSound播放音頻的流程

使用DirectSound播放音頻1般情況下需要以下步驟:

1. 初始化
1) 創(chuàng)建1個IDirectSound8接口的對象
2) 設置協作級
3) 創(chuàng)建1個主緩沖對象
4) 創(chuàng)建1個副緩沖對象
5) 創(chuàng)建通知對象
6) 設置通知位置

7) 開始播放

2. 循環(huán)播放聲音
1) 數據填充至副緩沖區(qū)

2) 等待播放完成

下面結合詳細分析1下上文的流程。


1. 初始化
1) 創(chuàng)建1個IDirectSound8接口的對象

通過DirectSoundCreate8()方法可以創(chuàng)建1個裝備對象。這個對象通常代表缺省的播放裝備。DirectSoundCreate8()函數定義以下。
HRESULT DirectSoundCreate8( LPCGUID lpcGuidDevice, LPDIRECTSOUND8 * ppDS8, LPUNKNOWN pUnkOuter )

參數的含義以下:
lpcGuidDevice:要創(chuàng)建的裝備對象的GUID。可以指定為NULL,代表默許的播放裝備。
ppDS8:返回的IDirectSound8對象的地址。
pUnkOuter:必須設為NULL。
例如以下代碼便可創(chuàng)建1個IDirectSound8接口的對象
IDirectSound8 *m_pDS=NULL; DirectSoundCreate8(NULL,&m_pDS,NULL);

2) 設置協作級
Windows 是1個多任務環(huán)境,同1時間有多個利用程序去訪問裝備。通過使用協作級別,DirectSound可以確保利用程序不會在別的裝備使用時去訪問,每一個 DirectSound利用程序都有1個協作級別,這個級別決定著訪問硬件的權限。
在創(chuàng)建1個裝備對象以后,必須通過用IDirectSound8的SetCooperativeLevel()設置協作權限,否則將聽不到聲音。SetCooperativeLevel()的定義以下
HRESULT SetCooperativeLevel( HWND hwnd, DWORD dwLevel )

參數的含義以下:
hwnd:利用程序窗口句柄。
dwLevel:支持以下幾種級別。
DSSCL_EXCLUSIVE:與DSSCL_PRIORITY具有相同的作用。
DSSCL_NORMAL:正常的調和層級標志,其他程序可同享聲卡裝備進行播放。
DSSCL_PRIORITY:設置聲卡裝備為當前程序獨占。
DSSCL_WRITEPRIMAR:可寫主緩沖區(qū),此時副緩沖區(qū)就不能進行播放處理,即不能將次緩沖區(qū)的數據送進混聲器,再輸出到主緩沖區(qū)上。這是最完全控制聲音播放的方式。


3) 創(chuàng)建1個主緩沖對象
使用IDirectSound8的CreateSoundBuffer()可以創(chuàng)建1個IDirectSoundBuffer接口的主緩沖區(qū)對象。CreateSoundBuffer()的定義以下。
HRESULT CreateSoundBuffer( LPCDSBUFFERDESC pcDSBufferDesc, LPDIRECTSOUNDBUFFER * ppDSBuffer, LPUNKNOWN pUnkOuter )

參數的含義以下:
pcDSBufferDesc:描寫聲音緩沖的DSBUFFERDESC結構體的地址
ppDSBuffer:返回的IDirectSoundBuffer接口的對象的地址。
pUnkOuter:必須設置為NULL。
其中觸及到1個描寫聲音緩沖的結構體DSBUFFERDESC,該結構體的定義以下:
typedef struct _DSBUFFERDESC { DWORD dwSize; DWORD dwFlags; DWORD dwBufferBytes; DWORD dwReserved; LPWAVEFORMATEX lpwfxFormat; } DSBUFFERDESC

簡單解釋1下其中的變量的含義:
dwSize:結構體的大小。必須初始化該值。
dwFlags:設置聲音緩存的屬性。有很多選項,可以組合使用,就不逐一列出了。詳細的參數可以查看文檔。
dwBufferBytes:緩沖的大小。
dwReserved:保存參數,暫時沒有用。
lpwfxFormat:指向1個WAVE格式文件頭的指針。
設置DSBUFFERDESC終了后,就能夠使用CreateSoundBuffer()創(chuàng)建主緩沖了。示例代碼以下:
DSBUFFERDESC dsbd; memset(&dsbd,0,sizeof(dsbd)); dsbd.dwSize=sizeof(dsbd); dsbd.dwFlags=DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY |DSBCAPS_GETCURRENTPOSITION2; dsbd.dwBufferBytes=MAX_AUDIO_BUF*BUFFERNOTIFYSIZE; //WAVE Header dsbd.lpwfxFormat=(WAVEFORMATEX*)malloc(sizeof(WAVEFORMATEX)); dsbd.lpwfxFormat->wFormatTag=WAVE_FORMAT_PCM; /* format type */ (dsbd.lpwfxFormat)->nChannels=channels; /* number of channels (i.e. mono, stereo...) */ (dsbd.lpwfxFormat)->nSamplesPerSec=sample_rate; /* sample rate */ (dsbd.lpwfxFormat)->nAvgBytesPerSec=sample_rate*(bits_per_sample/8)*channels; /* for buffer estimation */ (dsbd.lpwfxFormat)->nBlockAlign=(bits_per_sample/8)*channels; /* block size of data */ (dsbd.lpwfxFormat)->wBitsPerSample=bits_per_sample; /* number of bits per sample of mono data */ (dsbd.lpwfxFormat)->cbSize=0; //Creates a sound buffer object to manage audio samples. HRESULT hr1; if( FAILED(m_pDS->CreateSoundBuffer(&dsbd,&m_pDSBuffer,NULL))){ return FALSE; }


4) 創(chuàng)建1個副緩沖對象
使用IDirectSoundBuffer的QueryInterface()可以得到1個IDirectSoundBuffer8接口的對象。IDirectSoundBuffer8的GUID為IID_IDirectSoundBuffer8。示例代碼以下。
IDirectSoundBuffer *m_pDSBuffer=NULL; IDirectSoundBuffer8 *m_pDSBuffer8=NULL; ... if( FAILED(m_pDSBuffer->QueryInterface(IID_IDirectSoundBuffer8,(LPVOID*)&m_pDSBuffer8))){ return FALSE ; }


5) 創(chuàng)建通知對象
使用IDirectSoundBuffer8的QueryInterface()可以得到1個IDirectSoundNotify8接口的對象。IDirectSoundBuffer8的GUID為IID_IDirectSoundNotify。示例代碼以下。
IDirectSoundBuffer8 *m_pDSBuffer8=NULL; IDirectSoundNotify8 *m_pDSNotify=NULL; … if(FAILED(m_pDSBuffer8->QueryInterface(IID_IDirectSoundNotify,(LPVOID*)&m_pDSNotify))){ return FALSE ; }


1句話概括1下通知對象的作用:當DirectSound緩沖區(qū)中的數據播放終了后,告知系統(tǒng)應當填充新的數據。


6) 設置通知位置
使用IDirectSoundNotify8的SetNotificationPositions()可以設置通知的位置。SetNotificationPositions()的定義以下。
HRESULT SetNotificationPositions( DWORD dwPositionNotifies, LPCDSBPOSITIONNOTIFY pcPositionNotifies )

參數含義以下。
dwPositionNotifies:DSBPOSITIONNOTIFY結構體的數量。既包括幾個通知的位置。
pcPositionNotifies:指向DSBPOSITIONNOTIFY結構體數組的指針。
再這里觸及到1個結構體DSBPOSITIONNOTIFY,它描寫了通知的位置。DSBPOSITIONNOTIFY的定義以下。
typedef struct DSBPOSITIONNOTIFY { DWORD dwOffset; HANDLE hEventNotify; } DSBPOSITIONNOTIFY;

它的成員的含義以下。
dwOffset:通知事件觸發(fā)的位置(距離緩沖開始位置的偏移量)。
hEventNotify:觸發(fā)的事件的句柄。


7) 開始播放
使用IDirectSoundBuffer8的SetCurrentPosition ()可以設置播放的位置。SetCurrentPosition ()定義以下
HRESULT SetCurrentPosition( DWORD dwNewPosition )

其中dwNewPosition是播放點與緩沖區(qū)首個字節(jié)之間的偏移量。
使用IDirectSoundBuffer8的Play ()可以開始播放音頻數據。Play ()定義以下。
HRESULT Play( DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags )

參數含義:
dwReserved1:保存參數,必須取0。
dwPriority:優(yōu)先級,1般情況下取0便可。
dwFlags:標志位。目前常見的是DSBPLAY_LOOPING。當播放至緩沖區(qū)結尾的時候,重新從緩沖區(qū)開始處開始播放。


2. 循環(huán)播放聲音
1) 數據填充至副緩沖區(qū)

數據填充至副緩沖區(qū)之前,需要先使用Lock()鎖定緩沖區(qū)。然后就能夠使用fread(),memcpy()等方法將PCM音頻采樣數據填充至緩沖區(qū)。數據填充終了后,使用Unlock()取消對緩沖區(qū)的鎖定。
Lock()函數的定義以下。
HRESULT Lock( DWORD dwOffset, DWORD dwBytes, LPVOID * ppvAudioPtr1, LPDWORD pdwAudioBytes1, LPVOID * ppvAudioPtr2, LPDWORD pdwAudioBytes2, DWORD dwFlags )

參數的含義以下。
dwOffset:鎖定的內存與緩沖區(qū)首地址之間的偏移量。
dwBytes:鎖定的緩存的大小。
ppvAudioPtr1:獲得到的指向緩存數據的指針。
pdwAudioBytes1:獲得到的緩存數據的大小。
ppvAudioPtr2:沒有用到,設置為NULL。
pdwAudioBytes2:沒有用到,設置為0。
dwFlags:暫時沒有研究。


UnLock()函數的定義以下。
HRESULT Unlock( LPVOID pvAudioPtr1, DWORD dwAudioBytes1, LPVOID pvAudioPtr2, DWORD dwAudioBytes2 )

參數含義以下。
pvAudioPtr1:通過Lock()獲得到的指向緩存數據的指針。
dwAudioBytes1:寫入的數據量。
pvAudioPtr2:沒有用到。

dwAudioBytes2:沒有用到。

2) 等待播放完成

根據此前設置的通知機制,使用WaitForMultipleObjects()等待緩沖區(qū)中的數據播放終了,然落后入下1個循環(huán)。


播放音頻流程總結

DirectSound播放PCM音頻數據的流程以下圖所示。

 


其中觸及到的幾個結構體之間的關系以下圖所示。
 




代碼

貼上源代碼。

/** * 最簡單的DirectSound播放音頻的例子(DirectSound播放PCM) * Simplest Audio Play DirectSound (DirectSound play PCM) * * 雷霄驊 Lei Xiaohua * leixiaohua1020@126.com * 中國傳媒大學/數字電視技術 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * 本程序使用DirectSound播放PCM音頻采樣數據。 * 是最簡單的DirectSound播放音頻的教程。 * * 函數調用步驟以下: * * [初始化] * DirectSoundCreate8(): 創(chuàng)建1個DirectSound對象。 * SetCooperativeLevel(): 設置協作權限,不然沒有聲音。 * IDirectSound8->CreateSoundBuffer(): 創(chuàng)建1個主緩沖區(qū)對象。 * IDirectSoundBuffer->QueryInterface(IID_IDirectSoundBuffer8..): * 創(chuàng)建1個副緩沖區(qū)對象,用來存儲要播放的聲音數據文件。 * IDirectSoundBuffer8->QueryInterface(IID_IDirectSoundNotify..): * 創(chuàng)建通知對象,通知利用程序指定播放位置已到達。 * IDirectSoundNotify8->SetNotificationPositions(): 設置通知位置。 * IDirectSoundBuffer8->SetCurrentPosition(): 設置播放的起始點。 * IDirectSoundBuffer8->Play(): 開始播放。 * * [循環(huán)播放數據] * IDirectSoundBuffer8->Lock(): 鎖定副緩沖區(qū),準備寫入數據。 * fread(): 讀取數據。 * IDirectSoundBuffer8->Unlock(): 解鎖副緩沖區(qū)。 * WaitForMultipleObjects(): 等待“播放位置已到達”的通知。 * * This software plays PCM raw audio data using DirectSound. * It's the simplest tutorial about DirectSound. * * The process is shown as follows: * * [Init] * DirectSoundCreate8(): Init DirectSound object. * SetCooperativeLevel(): Must set, or we won't hear sound. * IDirectSound8->CreateSoundBuffer(): Create primary sound buffer. * IDirectSoundBuffer->QueryInterface(IID_IDirectSoundBuffer8..): * Create secondary sound buffer. * IDirectSoundBuffer8->QueryInterface(IID_IDirectSoundNotify..): * Create Notification object. * IDirectSoundNotify8->SetNotificationPositions(): * Set Notification Positions. * IDirectSoundBuffer8->SetCurrentPosition(): Set position to start. * IDirectSoundBuffer8->Play(): Begin to play. * * [Loop to play data] * IDirectSoundBuffer8->Lock(): Lock secondary buffer. * fread(): get PCM data. * IDirectSoundBuffer8->Unlock(): UnLock secondary buffer. * WaitForMultipleObjects(): Wait for Notifications. */ #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <dsound.h> #define MAX_AUDIO_BUF 4 #define BUFFERNOTIFYSIZE 192000 int sample_rate=44100; //PCM sample rate int channels=2; //PCM channel number int bits_per_sample=16; //bits per sample BOOL main(int argc,char * argv[]) { int i; FILE * fp; if((fp=fopen("../NocturneNo2inEflat_44.1k_s16le.pcm","rb"))==NULL){ printf("cannot open this file "); return ⑴; } IDirectSound8 *m_pDS=NULL; IDirectSoundBuffer8 *m_pDSBuffer8=NULL; //used to manage sound buffers. IDirectSoundBuffer *m_pDSBuffer=NULL; IDirectSoundNotify8 *m_pDSNotify=NULL; DSBPOSITIONNOTIFY m_pDSPosNotify[MAX_AUDIO_BUF]; HANDLE m_event[MAX_AUDIO_BUF]; SetConsoleTitle(TEXT("Simplest Audio Play DirectSound"));//Console Title //Init DirectSound if(FAILED(DirectSoundCreate8(NULL,&m_pDS,NULL))) return FALSE; if(FAILED(m_pDS->SetCooperativeLevel(FindWindow(NULL,TEXT("Simplest Audio Play DirectSound")),DSSCL_NORMAL))) return FALSE; DSBUFFERDESC dsbd; memset(&dsbd,0,sizeof(dsbd)); dsbd.dwSize=sizeof(dsbd); dsbd.dwFlags=DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY |DSBCAPS_GETCURRENTPOSITION2; dsbd.dwBufferBytes=MAX_AUDIO_BUF*BUFFERNOTIFYSIZE; //WAVE Header dsbd.lpwfxFormat=(WAVEFORMATEX*)malloc(sizeof(WAVEFORMATEX)); dsbd.lpwfxFormat->wFormatTag=WAVE_FORMAT_PCM; /* format type */ (dsbd.lpwfxFormat)->nChannels=channels; /* number of channels (i.e. mono, stereo...) */ (dsbd.lpwfxFormat)->nSamplesPerSec=sample_rate; /* sample rate */ (dsbd.lpwfxFormat)->nAvgBytesPerSec=sample_rate*(bits_per_sample/8)*channels; /* for buffer estimation */ (dsbd.lpwfxFormat)->nBlockAlign=(bits_per_sample/8)*channels; /* block size of data */ (dsbd.lpwfxFormat)->wBitsPerSample=bits_per_sample; /* number of bits per sample of mono data */ (dsbd.lpwfxFormat)->cbSize=0; //Creates a sound buffer object to manage audio samples. HRESULT hr1; if( FAILED(m_pDS->CreateSoundBuffer(&dsbd,&m_pDSBuffer,NULL))){ return FALSE; } if( FAILED(m_pDSBuffer->QueryInterface(IID_IDirectSoundBuffer8,(LPVOID*)&m_pDSBuffer8))){ return FALSE ; } //Get IDirectSoundNotify8 if(FAILED(m_pDSBuffer8->QueryInterface(IID_IDirectSoundNotify,(LPVOID*)&m_pDSNotify))){ return FALSE ; } for(i =0;i<MAX_AUDIO_BUF;i++){ m_pDSPosNotify[i].dwOffset =i*BUFFERNOTIFYSIZE; m_event[i]=::CreateEvent(NULL,false,false,NULL); m_pDSPosNotify[i].hEventNotify=m_event[i]; } m_pDSNotify->SetNotificationPositions(MAX_AUDIO_BUF,m_pDSPosNotify); m_pDSNotify->Release(); //Start Playing BOOL isPlaying =TRUE; LPVOID buf=NULL; DWORD buf_len=0; DWORD res=WAIT_OBJECT_0; DWORD offset=BUFFERNOTIFYSIZE; m_pDSBuffer8->SetCurrentPosition(0); m_pDSBuffer8->Play(0,0,DSBPLAY_LOOPING); //Loop while(isPlaying){ if((res >=WAIT_OBJECT_0)&&(res <=WAIT_OBJECT_0+3)){ m_pDSBuffer8->Lock(offset,BUFFERNOTIFYSIZE,&buf,&buf_len,NULL,NULL,0); if(fread(buf,1,buf_len,fp)!=buf_len){ //File End //Loop: fseek(fp, 0, SEEK_SET); fread(buf,1,buf_len,fp); //Close: //isPlaying=0; } m_pDSBuffer8->Unlock(buf,buf_len,NULL,0); offset+=buf_len; offset %= (BUFFERNOTIFYSIZE * MAX_AUDIO_BUF); printf("this is %7d of buffer ",offset); } res = WaitForMultipleObjects (MAX_AUDIO_BUF, m_event, FALSE, INFINITE); } return 0; }


運行結果

代碼運行以后,會彈出1個“控制臺”對話框以下圖所示。同時音頻裝備里面可以聽到播放的聲音。



 

下載

代碼位于“Simplest Media Play”中


SourceForge項目地址:https://sourceforge.net/projects/simplestmediaplay/

CSDN下載地址:http://download.csdn.net/detail/leixiaohua1020/8054395


上述工程包括了使用各種API(Direct3D,OpenGL,GDI,DirectSound,SDL2)播放多媒體例子。其中音頻輸入為PCM采樣數據。輸出至系統(tǒng)的聲卡播放出來。視頻輸入為YUV/RGB像素數據。輸出至顯示器上的1個窗口播放出來。
通過本工程的代碼初學者可以快速學習使用這幾個API播放視頻和音頻的技術。
1共包括了以下幾個子工程:
simplest_audio_play_directsound: 使用DirectSound播放PCM音頻采樣數據。
simplest_audio_play_sdl2: 使用SDL2播放PCM音頻采樣數據。
simplest_video_play_direct3d: 使用Direct3D的Surface播放RGB/YUV視頻像素數據。
simplest_video_play_direct3d_texture:使用Direct3D的Texture播放RGB視頻像素數據。
simplest_video_play_gdi: 使用GDI播放RGB/YUV視頻像素數據。
simplest_video_play_opengl: 使用OpenGL播放RGB/YUV視頻像素數據。
simplest_video_play_opengl_texture: 使用OpenGL的Texture播放YUV視頻像素數據。
simplest_video_play_sdl2: 使用SDL2播放RGB/YUV視頻像素數據。



生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 欧美xxxxx九色视频免费观看 | 2020国产成人免费视频 | 欧美福利网 | 最近中文字幕免费2019高清 | 国产亚洲精品资源一区 | 天天噜天天爽在线视频 | 午夜肉伦伦影院 | 久久精品免观看国产成人 | 成人爱爱网站在线观看 | 国产免费亚洲 | 午夜刺激 | 精品国产美女福利在线 | 最近中文字幕免费视频 | 欧美亚洲精品在线 | 欧美成视频在线观看 | 亚欧乱色| 久久久久久岛国免费网站 | 中文字幕亚洲无线码a | 国产精品永久免费自在线观看 | 欧美6699| jizzjizz大全 | 在线91色| 欧美一级毛片无遮挡内谢 | 亚洲欧美一区二区三区不卡 | 国产在线精品福利91香蕉 | 级毛片久久久毛片精品毛片 | 国内精品久久久久久中文字幕 | 自拍偷拍亚洲第一页 | 久久久无码精品亚洲日韩按摩 | 国产91精品高清一区二区三区 | 久久久www成人免费精品 | 最近的中文字幕在线国语 | 亚洲天码中字 | 乱人伦精品一区二区 | 性欧美18一19sex高清 | 国产日韩在线观看视频 | 亚洲三级色 | 中文字幕在线天堂 | 久久精品欧美一区二区 | 亚洲欧美日本韩国 | 亚洲人成在线影院 |