一 什麼是音頻的採樣率和採樣大小
自然界中的聲音非常複雜,波形極其複雜,通常我們採用的是脈衝代碼調製編碼。即PCM編碼。PCM通過抽樣、量化、編碼三個步驟將連續變化的模擬信號轉換爲數字編碼。
抽樣:在音頻採集中叫做採樣率。 由於聲音其實是一種能量波,因此也有頻率和振幅的特徵,頻率對應於時間軸線,振幅對應於電平軸線。波是無限光滑的,絃線可以看成由無數點組成,由於存儲空間是相對有限的,數字編碼過程中,必須對絃線的點進行採樣。採樣的過程就是抽取某點的頻率值,很顯然,在一秒中內抽取的點越多,獲取得頻率信息更豐富,爲了復原波形,一次振動中,必須有2個點的採樣,人耳能夠感覺到的最高頻率爲20kHz,因此要滿足人耳的聽覺要求,則需要至少每秒進行40k次採樣,用40kHz表達,這個40kHz就是採樣率。我們常見的CD,採樣率爲44.1kHz。 量化:我們這裏的採樣大小就是量化的過程,將該頻率的能量值並量化,用於表示信號強度。量化電平數爲 2的整數次冪,我們常見的CD位16bit的採樣大小,即2的16次方。 編碼: 根據採樣率和採樣大小可以得知,相對自然界的信號,音頻編碼最多隻能做到無限接近,至少目前的技術只能這樣了,相對自然界的信號,任何數字音頻編碼方案都是有損的,因爲無法完全還原。在計算機應用中,能夠達到最高保真水平的就是PCM編碼,被廣泛用於素材保存及音樂欣賞,CD、DVD以及我們常見的WAV文件中均有應用。因此,PCM約定俗成了無損編碼,因爲PCM代表了數字音頻中最佳的保真水準,並不意味着PCM就能夠確保信號絕對保真,PCM也只能做到最大程度的無限接近。我們而習慣性的把MP3列入有損音頻編碼範疇,是相對PCM編碼的。強調編碼的相對性的有損和無損,是爲了告訴大家,要做到真正的無損是困難的,就像用數字去表達圓周率,不管精度多高,也只是無限接近,而不是真正等於圓周率的值 爲什麼要使用音頻壓縮技術 要算一個PCM音頻流的碼率是一件很輕鬆的事情,採樣率值×採樣大小值×聲道數bps。一個採樣率爲44.1KHz,採樣大小爲16bit,雙聲道的PCM編碼的WAV文件,它的數據速率則爲 44.1K×16×2 =1411.2 Kbps。我們常說128K的MP3,對應的WAV的參數,就是這個1411.2 Kbps,這個參數也被稱爲數據帶寬,它和ADSL中的帶寬是一個概念。將碼率除以8,就可以得到這個WAV的數據速率,即176.4KB/s。這表示存儲一秒鐘採樣率爲44.1KHz,採樣大小爲16bit,雙聲道的PCM編碼的音頻信號,需要176.4KB的空間,1分鐘則約爲10.34M,這對大部分用戶是不可接受的,尤其是喜歡在電腦上聽音樂的朋友,要降低磁盤佔用,只有2種方法,降低採樣指標或者壓縮。降低指標是不可取的,因此專家們研發了各種壓縮方案。由於用途和針對的目標市場不一樣,各種音頻壓縮編碼所達到的音質和壓縮比都不一樣,在後面的文章中我們都會一一提到。有一點是可以肯定的,他們都壓縮過。 頻率與採樣率的關係 採樣率表示了每秒對原始信號採樣的次數,我們常見到的音頻文件採樣率多爲44.1KHz,這意味着什麼呢?假設我們有2段正弦波信號,分別爲20Hz和20KHz,長度均爲一秒鐘,以對應我們能聽到的最低頻和最高頻,分別對這兩段信號進行 40KHz的採樣,我們可以得到一個什麼樣的結果呢?結果是:20Hz的信號每次振動被採樣了40K/20=2000次,而20K的信號每次振動只有2次採樣。顯然,在相同的採樣率下,記錄低頻的信息遠比高頻的詳細。這也是爲什麼有些音響發燒友指責CD有數碼聲不夠真實的原因,CD的44.1KHz採樣也無法保證高頻信號被較好記錄。要較好的記錄高頻信號,看來需要更高的採樣率,於是有些朋友在捕捉CD音軌的時候使用48KHz的採樣率,這是不可取的!這其實對音質沒有任何好處,對抓軌軟件來說,保持和CD提供的44.1KHz一樣的採樣率纔是最佳音質的保證之一,而不是去提高它。較高的採樣率只有相對模擬信號的時候纔有用,如果被採樣的信號是數字的,請不要去嘗試提高採樣率。 流特徵 隨着網絡的發展,人們對在線收聽音樂提出了要求,因此也要求音頻文件能夠一邊讀一邊播放,而不需要把這個文件全部讀出後然後回放,這樣就可以做到不用下載就可以實現收聽了。也可以做到一邊編碼一邊播放,正是這種特徵,可以實現在線的直播,架設自己的數字廣播電臺成爲了現實。 二 android中AudioRecord採集音頻的參數說明在android中採集音頻的api是android.media.AudioRecord類其中構造器的幾個參數就是標準的聲音採集參數 以下是參數的含義解釋 public AudioRecord (int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)Since: API Level 3 Class constructor. Parameters
//
音頻獲取源 private int audioSource
= MediaRecorder.AudioSource.MIC; //
設置音頻採樣率,44100是目前的標準,但是某些設備仍然支持22050,16000,11025 private static int sampleRateInHz
= 44100 ; //
設置音頻的錄製的聲道CHANNEL_IN_STEREO爲雙聲道,CHANNEL_CONFIGURATION_MONO爲單聲道 private static int channelConfig
= AudioFormat.CHANNEL_CONFIGURATION_MONO; //
音頻數據格式:PCM 16位每個樣本。保證設備支持。PCM 8位每個樣本。不一定能得到設備支持。 private static int audioFormat
= AudioFormat.ENCODING_PCM_16BIT; .getAbsolutePath() + "/test.pcm"); // 刪除錄音文件 if (file.exists()) file.delete(); // 創建錄音文件 try { file.createNewFile(); } catch (IOException e) { throw new IllegalStateException("Failed to create " + file.toString()); } try { // Create a DataOuputStream to write the audio data into the // saved file. FileOutputStream fos = new FileOutputStream(file);// 建立一個可存取字節的文件 // Create a new AudioRecord object to record the audio. // 獲得滿足條件的最小緩衝區大小 bufferSizeInBytes = AudioRecord.getMinBufferSize( sampleRateInHz, channelConfig, audioFormat); // 創建AudioRecord對象 audioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes); byte[] buffer = new byte[bufferSizeInBytes]; audioRecord.startRecording(); isRecording = true; while (isRecording) { audioRecord.read(buffer, 0, bufferSizeInBytes); fos.write(buffer); } audioRecord.stop(); audioRecord.stop(); audioRecord.release();// 釋放資源 audioRecord = null; fos.close(); } catch (Throwable t) { Log.e("AudioRecord", "Recording Failed"); } // 放音的文件
File file = new File(Environment.getExternalStorageDirectory() .getAbsolutePath() + "/test.pcm"); FileInputStream in = null; try { in = new FileInputStream(file); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 獲得滿足條件的最小緩衝區大小 bufferSizeInBytes = AudioRecord.getMinBufferSize( sampleRateInHz, channelConfig, audioFormat); byte[] buffer = new byte[bufferSizeInBytes]; int byteread=0; AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM); // 放音 audioTrack.play(); try { while ((byteread = in.read(buffer)) != -1) { System.out.write(buffer, 0, byteread); System.out.flush(); audioTrack.write(buffer, 0, bufferSizeInBytes); } } catch (Exception e) { Log.e("AudioTrack", "Playback Failed"); } |