Windows錄音API學習筆記--轉

來源:互聯網
上載者:User

標籤:sizeof   建立   第一個   nbsp   系統   nop   應用程式   參數   演算法   

Windows錄音API學習筆記

結構體和函數資訊 

結構體

WAVEINCAPS

該結構描述了一個波形音訊輸入裝置的能力。

typedef struct {

    WORD      wMid; 用于波形音訊輸入裝置的裝置驅動程式製造商標識符。

    WORD      wPid; 聲音輸入裝置的產品識別碼。

    MMVERSION vDriverVersion; 用于波形音訊輸入裝置的裝置驅動程式的版本號碼。高位位元組是主要版本號,低位元組是次版本號碼。

    CHAR      szPname[MAXPNAMELEN]; 裝置名稱

    DWORD     dwFormats; 所支援的標準格式。可以是以下組合:

    WORD      wChannels; 數值指定裝置是否支援單(1)或立體聲(2)的輸入

    WORD      wReserved1; 填充

} WAVEINCAPS;

 

HWAVEIN 目前推測是開啟聲音裝置後擷取的控制代碼(現在看就是控制代碼)

 

WAVEHDR

此結構定義用於標識一個波形音頻緩衝器中的前序。

typedef struct { 
    LPSTR  lpData; 指向波形緩衝區。
    DWORD  dwBufferLength; 緩衝區長度
    DWORD  dwBytesRecorded; 被用於輸入時緩衝區的資料長度
    DWORD  dwUser; 使用者資料
    DWORD  dwFlags; 標誌提供有關緩衝區的資訊。詳見MSDN
    DWORD  dwLoops; 迴圈播放的次數,僅用於輸出緩衝器
    struct wavehdr_tag * lpNext; 保留
    DWORD  reserved; 保留
} WAVEHDR; 

 

WAVEFORMATEX

該結構定義的波形的音頻資料的格式。只有共同所有波形的音頻資料格式的格式資訊被包括在這種結構。對於需要更多的資訊格式,該結構被包括在另一種結構第一組件,伴隨著的附加資訊。

typedef struct { 
    WORD  wFormatTag; 波形音頻格式類型。格式標記註冊的微軟公司的很多壓縮演算法。格式標籤的完整列表可以在MMREG.H標頭檔中找到。
    WORD  nChannels; 在波形的音頻資料的通道數。單聲道的資料使用一個通道,立體聲資料使用兩個通道。
    DWORD nSamplesPerSec; 採樣速率,以每秒(赫茲)的樣品,每個通道應被播放或錄製。如果wFormatTag是WAVE_FORMAT_PCM,然後nSamplesPerSec共同的價值觀是8.0千赫,11.025千赫,22.05 kHz和44.1 kHz的。對於非PCM格式,這件必鬚根據製造商的格式標記的規範來計算。
    DWORD nAvgBytesPerSec; 所需的平均資料轉送速率,以每秒位元組的格式標記。如果wFormatTag是WAVE_FORMAT_PCM,nAvgBytesPerSec應等於nSamplesPerSec和nBlockAlign的乘積。對於非PCM格式,這件必鬚根據製造商的格式標記的規範來計算。
播放和錄製軟體,可以通過使用nAvgBytesPerSec成員估計緩衝區大小。
WORD  nBlockAlign; 
塊對齊,以位元組為單位。塊對齊用於wFormatTag格式類型資料的最小基本單位。如果wFormatTag是WAVE_FORMAT_PCM,nBlockAlign應等於nChannels和wBitsPerSample的乘積/8(每位元組位元)。對於非PCM格式,這件必鬚根據製造商的格式標記的規範來計算。
播放和錄製軟體必須處理的資料nBlockAlign位元組的倍數的時間。書面和從裝置讀取資料必須開始於一個塊的開始。例如,它是非法的開始播放在樣品的中間PCM資料(也就是,在一個非塊對齊的邊界)。
WORD  wBitsPerSample; 
採樣位元用於wFormatTag格式類型。如果wFormatTag是WAVE_FORMAT_PCM,然後wBitsPerSample應等於8或16。對於非PCM格式,這件必鬚根據製造商的格式標記的規範來設定。請注意,某些壓縮方案不能定義為wBitsPerSample一個值,因此該構件可以是零。
WORD  cbSize; 
大小以位元組為單位的額外格式資訊追加到WAVEFORMATEX結構的末端。此資訊可用於通過非PCM格式來儲存額外用於wFormatTag屬性。如果沒有額外的資訊所必需的wFormatTag,這個組件必須被設定為零。請注意,對於WAVE_FORMAT_PCM格式(只有WAVE_FORMAT_PCM格式),這個成員被忽略。
} WAVEFORMATEX; 

 

函數資訊

//這裡擷取到聲音裝置控制代碼使用的是waveInOpen函數,在那裡進行的初始化。

 

返回裝置的數量。返回的零值表示沒有裝置存在或發生了錯誤。

UINT waveInGetNumDevs(VOID); 

 

該函數檢索一個給定的波形音訊輸入裝置的能力。

MMRESULT waveInGetDevCaps(

  UINTuDeviceID,     標識符波形音訊輸出的裝置。它可以是一個裝置標識符或一個開放波形音訊輸入裝置的一個控制代碼。

  LPWAVEINCAPSpwic,  指向一個WAVEINCAPS結構填充有關器件的資訊的功能

  UINTcbwic        WAVEINCAPS結構體的位元組數

);

 

MMRESULT waveInOpen(

  LPHWAVEINphwi,指向開啟的聲音裝置的標識控制代碼。在fdwOpen參數指定為WAVE_FORMAT_QUERY時這個參數可以為空白

  UINTuDeviceID, 要開啟裝置的標識符

  LPWAVEFORMATEXpwfx, 指向一個WAVEFORMATEX結構,它標識用於記錄波形音頻資料所需的格式。您可以waveInOpen返回後立即釋放此結構。

  DWORDdwCallback, 指標指向一個固定的回呼函數,事件控制代碼,控制代碼到視窗或線程波形音頻錄製過程中被調用來處理與記錄的進度訊息的標識符。如果沒有回調功能是必需的,則該值可以是零。       

  DWORDdwCallbackInstance,傳給回調機制的資料類型,此參數並不用於視窗回調

  DWORDfdwOpenz指明dwCallBack傳入的資料類型,比如時間控制代碼或者函數指標

);

 

該函數為音訊輸入裝置提供緩衝區

MMRESULT waveInPrepareHeader(

  HWAVEINhwi, 聲音輸入裝置控制代碼

  LPWAVEHDRpwh, 指向WAVEHDR結構,標識要準備的緩衝區。

  UINTcbwhWAVEHDR結構的大小

);

 

該函數發送一個輸入緩衝區給定的波形音訊輸入裝置。當緩衝區被填滿後,通知應用程式。

MMRESULT waveInAddBuffer(

  HWAVEINhwi, 聲音輸入裝置控制代碼

  LPWAVEHDRpwh, 指向WAVEHDR結構,標識要準備的緩衝區。

  UINTcbwhWAVEHDR結構的大小

);

 

該函數啟動指定裝置的輸入

MMRESULT waveInStart(

  HWAVEINhwi 裝置控制代碼

);

 

基於Windows API的錄音分析

   昨天看了一天的Audio API和windows的Wav檔案相關資料,漸漸的理清了一點思路,所以在此總結一下,待本文已完成之後,應該就能繼續下一步了。

    1 首先要瞭解的是電腦是如何表示音效檔的。

    我們熟知的音頻格式有:MP3,WMA,FLAC,以及WAV。這裡我暫時只關注WAV。要知道的是,WAV其實就是WAVE,意思為波形。真實世界中的聲音都是連續的,因為是類比訊號,但是在電腦中儲存的資訊都是數字訊號。所以在將聲音儲存到電腦之前,就必須要進行聲音的數字化,轉換成電腦能夠儲存的形式。

    學過訊號與系統的應該都知道,類比訊號轉換為數字訊號,一種比較通用的方法就是進行等間隔採樣。根據奈奎斯特定理,採樣頻率至少為訊號頻率的2倍,才能無失真的儲存原有的音頻訊號。因此採樣頻率的高低決定了數字訊號的逼真度,自然是越高越好。打個比方,一個周期為1ms的正弦訊號,采兩個點和采100個點的訊號在還原成類比訊號的時候,肯定是采100個點訊號的還原效果更好。

     在對類比訊號完成採樣後,得到的是一系列的離散電壓訊號。因此在將資料存放區至電腦前,還需要對這些類比訊號進行量化。這裡所謂量化,就是用位元據來表示電平的大小。一般採用8位(256級)或者16位(65536級)的資料來表示,在硬體級的設計中,需要根據ADC的具體情況來決定。而在Windows中,可以使用waveInGetDevCaps函數擷取音效卡資訊,以判斷使用8位或者16位的量化採樣位元。在其參數的dwFormats 成員中,包含了相應的資訊,具體如下:

 

    假設0-5V電平的正弦訊號,在採用8位的量化下,就將0-5V分成256個梯度,每個梯度的電壓差值為0.0195V。一個4V的採樣值對應的數值就是205(204.8)。

在經過上面兩步的處理之後,類比聲音訊號就轉化為了具有固定採樣頻率和相應量化標準的數字訊號。所以數字聲音訊號最主要的兩個參數就是採樣位寬和採樣頻率。

另外一個需要注意的就是聲道。我們平時聽到的音樂都是立體聲,也就是雙聲道的。這個在WAV檔案格式中有專門的介紹,不過我沒細看……記得是交替出現的。

現在回過頭來看一下上面的表,音效卡在提供訊號的時候,也就上面這三個主要因素:採樣頻率,聲道,量化位寬。

    我根據WAV檔案頭的資料寫了一份代碼,開啟了並查看了一個我電腦上的WAV格式的音樂檔案,以前在CD上拷的。

    運行結果如下:

 

    不過這個就是看一看,暫時還用不到,有了上面的基礎,接下來要做的事分析API的使用。

第一個要調用的函數:

UINT waveInGetNumDevs(VOID);

    這個函數的僅僅是用來查看電腦上是否有音效卡裝置,如果傳回值是0,那麼表明沒有音效卡。不過一般這種情況不會發生,畢竟現在的電腦主板都是整合音效卡的。

    第二個要調用的函數:

            MMRESULT waveInGetDevCaps(

           UINTuDeviceID,

              LPWAVEINCAPSpwic,

                           UINTcbwic);

    這個函數的第一個參數是裝置ID,不過我們現在並不知道裝置ID,但是沒關係,只要我們知道有裝置存在就可以了。不過在MSDN上有這麼一句讓我比較費解的話:

“Use this function to determine the number of waveform-audio input devices present in the system.”

中文意思就是:使用這個函數來決定系統中聲音輸入裝置的數量。我看這個函數名感覺更像是擷取裝置能力資訊,而且第二個參數更是直接點出了其目的,“聲音輸入裝置的能力”。

回來接著說第一個參數,MSDN在Remarks說明了這個參數可以是從0值到裝置數量中的任何一個數,或者使用WAVE_MAPPER。我估計這個WAVE_MAPPER就是0值,因為傳入的參數要求是個指標,所以,你懂的。因此只要填個0就可以了。不過至於多音效卡裝置具體怎麼玩,我還真的不太清楚。

第二個參數是一個指向WAVEINCAPS結構的指標,在這把這個結構好好剖析一下:

typedef struct { 
    WORD      wMid; 
    WORD      wPid; 
    MMVERSION vDriverVersion; 
    CHAR      szPname[MAXPNAMELEN]; 
    DWORD     dwFormats; 
    WORD      wChannels; 
    WORD      wReserved1; 
} WAVEINCAPS; 

前兩個結構體成員分別是裝置製造商資訊和產品標示符,我點擊去看了下,發現一堆的裝置製造商資訊的宏定義……不用太關心。

第三個成員是MMVERSION,其本質就是一個UINT,驅動裝置版本號碼,也不用太關心。

第四個成員是裝置名稱,就是一個以’\0’結尾的字串,同樣不用管太多。

第五個成員很重要,因為指明了支援音頻訊號標準的組合,具體看這個結構體的MSDN翻譯。

第六個是聲道支援的判斷。其實這個參數在第五個參數的分析中就能看出來的。

第七個是保留參數,不過至今還沒用上。

第三個參數僅僅是告知第二個參數對應結構體的大小。

根據程式的調試結果看,這個函數的用途更多是初始化WAVEINCAPS。根據下面的圖可以看到,在該函數調用完畢後,結構體被初始化。

第三個調用的函數:

MMRESULT waveInOpen(
LPHWAVEINphwi,
UINTuDeviceID,
LPWAVEFORMATEXpwfx,
DWORDdwCallback,
DWORDdwCallbackInstance,
DWORDfdwOpen
);

    這個函數很有意思,同時也相對比較複雜。這裡的第一個參數是裝置控制代碼,不過在這裡的這個參數是在這裡被初始化的,傳進來就可以了。第二個參數還是裝置ID,不過這個裝置ID好像也是只傳個0就行了。第三個參數需要好好研究一下,也就是這個結構,WAVEFORMATEX。我現在覺得是用來初始化裝置的。

先查看一下MSDN中對其的說明:

typedef struct { 
    WORD  wFormatTag; 
    WORD  nChannels; 
    DWORD nSamplesPerSec; 
    DWORD nAvgBytesPerSec; 
    WORD  nBlockAlign; 
    WORD  wBitsPerSample; 
    WORD  cbSize; 
} WAVEFORMATEX; 

第一個成員是格式型別參數,在第二個函數中,也就是waveInGetDevCaps函數的第二個參數的子成員Format,其組成基本是一致的。如果在這個參數中使用上面的宏定義指定,那麼後面的如nChannel參數函數不用處理了。不過一種比較通用的方式還是將wFormatTag修改為WAVE_FORMAT_PCM,並依此初始化後面的參數。

這第三個參數可以根據需求進行相應的初始化,比如這形式的:

WaveInitFormat(

WORD    nCh,

DWORD   nSampleRate, 

WORD    BitsPerSample)

{

      m_WaveFormat.wFormatTag = WAVE_FORMAT_PCM;

      m_WaveFormat.nChannels = nCh;

      m_WaveFormat.nSamplesPerSec = nSampleRate;

      m_WaveFormat.nAvgBytesPerSec = nSampleRate * nCh * BitsPerSample/8;

      m_WaveFormat.nBlockAlign = m_WaveFormat.nChannels * BitsPerSample/8;

      m_WaveFormat.wBitsPerSample = BitsPerSample;

      m_WaveFormat.cbSize = 0;

在這個初始化函數中,第一個是WAVE_FORMAT_PCM,第二個就是聲道數的選項了,第三個是採樣頻率,第四個參數是根據採樣頻率,聲道,每個採樣點位元組數的乘積計算的。第五個參數則是塊對齊大小,也就是每個獨立的聲音節點資訊的大小,其計算公式就是聲道數*每個採樣點的位元組數。第六個參數就是採樣點的位元,指定了聲音的量化水平。第七個參數有點說法,不過在wFormatTag被指定為WAVE_FORMAT_PCM的時候,這個參數會被忽略。

第四參數是回調處理參數,比如,一個回呼函數,或者事件控制代碼,或者線程控制代碼。用於通知應用程式處理音頻資料的一個參數。等作業系統的音頻訊號接收完畢,就是通過這個參數來決定以什麼方式來把資料傳遞給應用程式。

第五個參數是傳給回調機制的資料類型,並不用語視窗回調。這個參數我不太懂,但是使用的時候,傳入NULL就可以了。

第六個參數是開啟裝置的一個通知資訊,主要用途就是告訴函數上面第四個參數是一個回呼函數還是一個事件控制代碼。就這麼簡單。

 

第四個調用的函數是waveInPrepareHeader。這個函數的功能就是在初始化好後,為聲音輸入裝置準備一塊存放聲音資料的緩衝區。

MMRESULT waveInPrepareHeader(
  HWAVEINhwi,    
  LPWAVEHDRpwh
  UINTcbwh       

);

第一個參數就是裝置控制代碼,不多說。

第二個參數需要好好的關注一下,這一個結構體。

typedef struct { 

    LPSTR  lpData; 
    DWORD  dwBufferLength; 
    DWORD  dwBytesRecorded; 
    DWORD  dwUser; 
    DWORD  dwFlags; 
    DWORD  dwLoops; 
    struct wavehdr_tag * lpNext; 
    DWORD  reserved; 

} WAVEHDR;

         LpData就是一個緩衝區,指向你所提供的記憶體空間。至於具體大小,好像沒有什麼限制,因為填滿了會通知你,到時候用專門的函數去處理就OK了。

       DwBufferLength參數就是緩衝區長度,不多說。

       DwByteRecorded實際記錄的位元組數,因為未必每次都會用掉全部的緩衝區。

       DwUser使用者資料,具體幹啥,MSDN沒說,有可能是到時候傳給回呼函數的資訊,我猜的。

       DwFlags對於buffer的附加資訊,根據MSDN的要求,在使用這個函數waveInPrepareHeader的時候,這個參數必須為0。

       DwLoops迴圈播放的次數,只用於輸出使用。這裡用不到。

       LpNext 這個雖然是指標,但是實際確實一個保留參數。

       reserved最後一個和上面一樣。

    這樣這個結構體算是完事了。就是提供一個頭部輸入的緩衝區。

    第三個參數就是上面那個結構的位元組數。一個sizeof萬事大吉。

    這個函數的用途就是為聲音輸入裝置準備一個緩衝區,到時候音頻資訊就會被寫進這個記憶體地區內。

 

    第五個函數就是waveInAddBuffer,這個函數的用途就是把函數waveInPrepareHeader準備好的那個緩衝區發送給聲音輸入裝置。

MMRESULT waveInAddBuffer(

  HWAVEINhwi,   

  LPWAVEHDRpwh

  UINTcbwh      

);

    和上一個函數的原型是一致的,三個參數和waveInPrepareHeader函數使用過的參數必須一致。而且在本函數調用前,pwh參數的Buffer必須經過waveInPrepareHeader函數的處理。當緩衝區被填滿,那麼會通知應用程式。詳見MSDN。

   

    第六個函數是waveInStart,這個函數僅用於開啟錄音功能,最簡單。

 

    Windows的錄音流程大致如下:

    1 先查看本地機器是否擁有聲音輸入裝置。

    2 擷取聲音輸入裝置的資訊

    一般上面的兩部不是很必要,畢竟現在的電腦基本都擁有整合音效卡。不過從穩定性和通用性看,還是很必要的。

    3 開啟裝置,擷取裝置控制代碼,傳入對應的事件控制代碼。

4 準備一個非同步線程專門用於錄音完成後的處理工作,並等待事件。

    5 通過裝置控制代碼為其準備緩衝區

    6 將準備好的緩衝區通過控制代碼添加到裝置中

    接下來屬於系統的工作,正常情況下,在緩衝區被填滿後,將會觸發事件,來通知非同步線程進行處理。擷取聲音資訊後,要再次添加緩衝區,才能繼續錄音。

 

20140118 補充:

  有時在緩衝區的建立上,一般會採取棧分配或者堆分配的方式。棧分配記憶體的析構處理通過退棧完成,使用者不用手動處理。但是堆分配的時候就會遇到麻煩,具體如下:

  在準備緩衝區的時候會調用waveInPrepareHeader函數,這個函數調用後,為其分配的記憶體就無法通過delete或者free來釋放了,因為在該函數調用後這塊記憶體地區被鎖定了。此時必須調用waveInUnprepareHeader函數才能解鎖定,然後才能釋放。

  但是在調用waveInPrepareHeader函數後,再接著調用了waveInAddBuffer函數,在該緩衝區未被填滿的時候,嘗試使用waveInUnprepareHeader函數解鎖定,就會返回失敗碼33。這裡的解決方案就是在決定釋放空間前,首先調用一個函數:waveInReset。這個函數調用後,就可以將記憶體從waveInAddBuffer函數的限定中釋放,然後再常規使用waveInUnprepareHeader釋放,最後調用delete或者free釋放記憶體空間。

 

from:http://www.cnblogs.com/matrix-r/p/3523303.html

Windows錄音API學習筆記--轉

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.