日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android音视频专题(二) 在 Android 平台使用 AudioRecord 和 AudioTrack API 完成音频 PCM 数据的采集和播放,并实现读写音频 wav 文件

發布時間:2023/12/10 Android 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android音视频专题(二) 在 Android 平台使用 AudioRecord 和 AudioTrack API 完成音频 PCM 数据的采集和播放,并实现读写音频 wav 文件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、AudioTrack 基本使用

AudioTrack 類可以完成Android平臺上音頻數據的輸出任務。AudioTrack有兩種數據加載模式(MODE_STREAM和MODE_STATIC),對應的是數據加載模式和音頻流類型,?對應著兩種完全不同的使用場景。

  • MODE_STREAM:在這種模式下,通過write一次次把音頻數據寫到AudioTrack中。這和平時通過write系統調用往文件中寫數據類似,但這種工作方式每次都需要把數據從用戶提供的Buffer中拷貝到AudioTrack內部的Buffer中,這在一定程度上會使引入延時。為解決這一問題,AudioTrack就引入了第二種模式。
  • MODE_STATIC:這種模式下,在play之前只需要把所有數據通過一次write調用傳遞到AudioTrack中的內部緩沖區,后續就不必再傳遞數據了。這種模式適用于像鈴聲這種內存占用量較小,延時要求較高的文件。但它也有一個缺點,就是一次write的數據不能太多,否則系統無法分配足夠的內存來存儲全部數據。

1.1 MODE_STATIC模式

MODE_STATIC模式輸出音頻的方式如下(注意:如果采用STATIC模式,須先調用write寫數據,然后再調用play。):

public class AudioTrackPlayerDemoActivity extends Activity implementsOnClickListener {private static final String TAG = "AudioTrackPlayerDemoActivity";private Button button;private byte[] audioData;private AudioTrack audioTrack;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);super.setContentView(R.layout.main);this.button = (Button) super.findViewById(R.id.play);this.button.setOnClickListener(this);this.button.setEnabled(false);new AsyncTask<Void, Void, Void>() {@Overrideprotected Void doInBackground(Void... params) {try {InputStream in = getResources().openRawResource(R.raw.ding);try {ByteArrayOutputStream out = new ByteArrayOutputStream(264848);for (int b; (b = in.read()) != -1;) {out.write(b);}Log.d(TAG, "Got the data");audioData = out.toByteArray();} finally {in.close();}} catch (IOException e) {Log.wtf(TAG, "Failed to read", e);}return null;}@Overrideprotected void onPostExecute(Void v) {Log.d(TAG, "Creating track...");button.setEnabled(true);Log.d(TAG, "Enabled button");}}.execute();}public void onClick(View view) {this.button.setEnabled(false);this.releaseAudioTrack();this.audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT,audioData.length, AudioTrack.MODE_STATIC);Log.d(TAG, "Writing audio data...");this.audioTrack.write(audioData, 0, audioData.length);Log.d(TAG, "Starting playback");audioTrack.play();Log.d(TAG, "Playing");this.button.setEnabled(true);}private void releaseAudioTrack() {if (this.audioTrack != null) {Log.d(TAG, "Stopping");audioTrack.stop();Log.d(TAG, "Releasing");audioTrack.release();Log.d(TAG, "Nulling");}}public void onPause() {super.onPause();this.releaseAudioTrack();} }

1.2 MODE_STREAM模式

MODE_STREAM 模式輸出音頻的方式如下:

byte[] tempBuffer = new byte[bufferSize]; int readCount = 0; while (dis.available() > 0) {readCount = dis.read(tempBuffer);if (readCount == AudioTrack.ERROR_INVALID_OPERATION || readCount == AudioTrack.ERROR_BAD_VALUE) {continue;}if (readCount != 0 && readCount != -1) {audioTrack.play();audioTrack.write(tempBuffer, 0, readCount);} }?

二、AudioTrack 詳解

?2.1 ?音頻流的類型

在AudioTrack構造函數中,會接觸到AudioManager.STREAM_MUSIC這個參數。它的含義與Android系統對音頻流的管理和分類有關。

Android將系統的聲音分為好幾種流類型,下面是幾個常見的:

·? STREAM_ALARM:警告聲

·? STREAM_MUSIC:音樂聲,例如music等

·? STREAM_RING:鈴聲

·? STREAM_SYSTEM:系統聲音,例如低電提示音,鎖屏音等

·? STREAM_VOCIE_CALL:通話聲

注意:上面這些類型的劃分和音頻數據本身并沒有關系。例如MUSIC和RING類型都可以是某首MP3歌曲。另外,聲音流類型的選擇沒有固定的標準,例如,鈴聲預覽中的鈴聲可以設置為MUSIC類型。音頻流類型的劃分和Audio系統對音頻的管理策略有關。

?

2.2 Buffer分配和Frame的概念

在計算Buffer分配的大小的時候,我們經常用到的一個方法就是:getMinBufferSize。這個函數決定了應用層分配多大的數據Buffer。

AudioTrack.getMinBufferSize(8000,//每秒8K個采樣點 AudioFormat.CHANNEL_CONFIGURATION_STEREO,//雙聲道 AudioFormat.ENCODING_PCM_16BIT);

從AudioTrack.getMinBufferSize開始追溯代碼,可以發現在底層的代碼中有一個很重要的概念:Frame(幀)。Frame是一個單位,用來描述數據量的多少。1單位的Frame等于1個采樣點的字節數×聲道數(比如PCM16,雙聲道的1個Frame等于2×2=4字節)。1個采樣點只針對一個聲道,而實際上可能會有一或多個聲道。由于不能用一個獨立的單位來表示全部聲道一次采樣的數據量,也就引出了Frame的概念。Frame的大小,就是一個采樣點的字節數×聲道數。另外,在目前的聲卡驅動程序中,其內部緩沖區也是采用Frame作為單位來分配和管理的。

下面是追溯到的native層的方法:

// minBufCount表示緩沖區的最少個數,它以Frame作為單位uint32_t minBufCount = afLatency / ((1000 *afFrameCount)/afSamplingRate);if(minBufCount < 2) minBufCount = 2;//至少要兩個緩沖//計算最小幀個數uint32_tminFrameCount = (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;//下面根據最小的FrameCount計算最小的緩沖大小 intminBuffSize = minFrameCount //計算方法完全符合我們前面關于Frame的介紹* (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)* nbChannels;returnminBuffSize;

getMinBufSize會綜合考慮硬件的情況(諸如是否支持采樣率,硬件本身的延遲情況等)后,得出一個最小緩沖區的大小。一般我們分配的緩沖大小會是它的整數倍。

2.3?AudioTrack構造過程

每一個音頻流對應著一個AudioTrack類的一個實例,每個AudioTrack會在創建時注冊到 AudioFlinger中,由AudioFlinger把所有的AudioTrack進行混合(Mixer),然后輸送到AudioHardware中進行播放,目前Android同時最多可以創建32個音頻流,也就是說,Mixer最多會同時處理32個AudioTrack的數據流。?

三、?AudioTrack 與?MediaPlayer 的對比

播放聲音可以用MediaPlayer和AudioTrack,兩者都提供了Java?API供應用開發者使用。雖然都可以播放聲音,但兩者還是有很大的區別的。

3.1 區別

其中最大的區別是MediaPlayer可以播放多種格式的聲音文件,例如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer會在framework層創建對應的音頻解碼器。而AudioTrack只能播放已經解碼的PCM流,如果對比支持的文件格式的話則是AudioTrack只支持wav格式的音頻文件,因為wav格式的音頻文件大部分都是PCM流。AudioTrack不創建解碼器,所以只能播放不需要解碼的wav文件。

3.2 聯系

MediaPlayer在framework層還是會創建AudioTrack,把解碼后的PCM數流傳遞給AudioTrack,AudioTrack再傳遞給AudioFlinger進行混音,然后才傳遞給硬件播放,所以是MediaPlayer包含了AudioTrack。

3.3?SoundPool

在接觸Android音頻播放API的時候,發現SoundPool也可以用于播放音頻。下面是三者的使用場景:MediaPlayer 更加適合在后臺長時間播放本地音樂文件或者在線的流式資源; SoundPool 則適合播放比較短的音頻片段,比如游戲聲音、按鍵聲、鈴聲片段等等,它可以同時播放多個音頻; 而 AudioTrack 則更接近底層,提供了非常強大的控制能力,支持低延遲播放,適合流媒體和VoIP語音電話等場景。

四、源碼?

https://github.com/renhui/AudioDemo?

總結

以上是生活随笔為你收集整理的Android音视频专题(二) 在 Android 平台使用 AudioRecord 和 AudioTrack API 完成音频 PCM 数据的采集和播放,并实现读写音频 wav 文件的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。