最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android錄音mp3格式實例詳解

 更新時間:2018年03月26日 11:55:13   投稿:wdc  
本文講述了Android錄音成MP3格式實現思路概述,需要的朋友可以參考下

Android錄音支持的格式有amr、aac,但這兩種音頻格式在跨平臺上表現并不好。
MP3顯然才是跨平臺的最佳選擇。

項目地址

GavinCT/AndroidMP3Recorder

實現思路概述

在分析代碼前,我們需要明確幾個問題

1. 如何最終生成MP3

實現MP3格式最好是借助Lame這個成熟的解決方案。
對于Android來說,需要借助JNI來調用Lame的C語言代碼,實現音頻格式的轉化。

2. 如何獲取最初的音頻數據

AudioRecord類可以直接幫助我們獲取音頻數據。

3. 如何進行轉換

網上有代碼是先錄制后轉為MP3,這種效率比較低。因為如果錄音時間過長,轉換時間就會相應變長,用戶在存儲錄音時需要等待的時間就會變長。
Samsung Developers先錄后轉示例代碼
顯然,這種方案是不可取的。
我們需要的是邊錄邊轉的實現方式,這樣在停止錄音進行存儲的時候,就不會花費太長時間。

實現代碼介紹

既然是錄音,我們上面也提到了需要使用AudioRecord類,我們就從這個類的構造器開始說起

構造器

public AudioRecord (int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)

構造器參數很多,我們一點一點來看:

  • audioSource : 聲源,一般使用MediaRecorder.AudioSource.MIC表示來自于麥克風
  • sampleRateInHz :官方明確說到只有44100Hz是所有設備都支持的。其他22050、16000和11025只能在某些設備上使用。
  • channelConfig : 有立體聲(CHANNEL_IN_STEREO)和單聲道(CHANNEL_IN_MONO)兩種。但只有單聲道(CHANNEL_IN_MONO)是所有設備都支持的。
  • audioFormat : 有ENCODING_PCM_16BIT和ENCODING_PCM_8BIT兩種音頻編碼格式。同樣的,官方聲明只有ENCODING_PCM_16BIT是所有設備都支持的。
  • bufferSizeInBytes : 錄音期間聲音數據的寫入緩沖區(qū)大?。▎挝皇亲止?jié))。

其實從上面的解釋可以看到,類的參數很多,但為了保證在所有設備上可以使用,我們真正需要填寫的只有一個參數:bufferSizeInBytes,其他都可以使用通用的參數而不用自己費心來選擇。
在深究bufferSizeInBytes該傳入什么之前,我們先略過這一段,先來說一下錄音的讀取與轉換。

錄音的讀取與轉換策略

錄音的讀取其實和UDP差不多,需要不斷的讀取數據。
既然是不斷,那么我們當然需要循環(huán)讀取,意味著我們需要一個線程來單獨讀取錄音,避免阻塞主線程。
還和UDP差不多的是,如果不及時讀取,數據超過緩沖區(qū)大小,會造成這段錄音數據的丟失。
上面提到過,我們想要實現的是邊錄邊轉。那么問題來了,如果我們讀取完數據后接著將數據傳給Lame進行MP3編碼,Lame的編碼時間是不確定的,是不是有可能造成數據的丟失呢?
答案當然是有可能,所以我們不能巧合編程。
我們需要另外一個線程,即數據編碼線程來專門進行MP3編碼,而當前的錄音讀取線程只負責讀取錄音PCM數據。
有了兩條線程,我們還需要確認一點,什么時候編碼線程開始處理數據?

編碼線程處理數據的時機

傳統(tǒng)的方法是當線程中有數據的時候開始處理,這就需要在這個線程里面不斷循環(huán)查看是否有數據需要處理,有數據就開始處理,沒有數據我們可以暫時休息幾毫秒(當然一直不sleep也可以,但造成的系統(tǒng)消耗太多)。
這種方式顯然也是低效的,因為無論我們讓線程休息多久都可以判定為不合理。因為我們并不知道準確的時間。
那么還有別的方法么?
顯然錄音這個類是知道什么時候該處理數據,什么時候可以休息。
Don't call me , I will call you.
是的,我們應該去看看有沒有監(jiān)聽器,讓錄音來通知編碼線程開始工作。
AudioRecord為我們提供了這樣的方法:

public int setPositionNotificationPeriod (int periodInFrames)

Added in API level 3
Sets the period at which the listener is called, if set with setRecordPositionUpdateListener(OnRecordPositionUpdateListener) or setRecordPositionUpdateListener(OnRecordPositionUpdateListener, Handler). It is possible for notifications to be lost if the period is too small.

設置通知周期。 以幀為單位。

到這里,我們可以回來來解釋bufferSizeInBytes大小的傳入了。

緩沖區(qū)的大小

其實AudioRecord類提供了一個方便的方法getMinBufferSize來獲取緩沖區(qū)的大小。

public static int getMinBufferSize (int sampleRateInHz, int channelConfig, int audioFormat)

這里的3個參數,其實我們都可以從構造器的參數里看到,因此傳入并沒有什么問題。
但關鍵在如上面我們設置了周期單位,如果獲得的緩沖區(qū)大小不是周期單位的整數倍呢?
不是整數倍當然會如我們猜想的一樣造成數據丟失,因此我們還需要一些數據的糾正來保證緩沖區(qū)大小是整數倍。

mBufferSize = AudioRecord.getMinBufferSize(DEFAULT_SAMPLING_RATE,
    DEFAULT_CHANNEL_CONFIG, DEFAULT_AUDIO_FORMAT.getAudioFormat());

int bytesPerFrame = DEFAULT_AUDIO_FORMAT.getBytesPerFrame();
/* Get number of samples. Calculate the buffer size 
 * (round up to the factor of given frame size) 
 * 使能被整除,方便下面的周期性通知
 * */
int frameSize = mBufferSize / bytesPerFrame;
if (frameSize % FRAME_COUNT != 0) {
  frameSize += (FRAME_COUNT - frameSize % FRAME_COUNT);
  mBufferSize = frameSize * bytesPerFrame;
}

講完了數據的獲取線程和編碼線程,我們來仔細看看幫助我們實現MP3編碼的功臣:Lame

Lame的獲取與編譯

Lame在線下載地址

步驟

解壓libmp3lame 到jni目錄.
拷貝 lame.h (include目錄下)
創(chuàng)建Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE  := mp3lame
LOCAL_SRC_FILES := bitstream.c fft.c id3tag.c mpglib_interface.c presets.c quantize.c  reservoir.c tables.c util.c VbrTag.c encoder.c gain_analysis.c lame.c newmdct.c  psymodel.c quantize_pvt.c set_get.c takehiro.c vbrquantize.c version.c
include $(BUILD_SHARED_LIBRARY)


刪除非.c/.h文件:GNU autotools, Makefile.am Makefile.in libmp3lame_vc8.vcproj logoe.ico depcomp, folders i386 等無用文件。
編輯 jni/utils.h。把extern ieee754_float32_t fast_log2(ieee754_float32_t x);替換為extern float fast_log2(float x);。如果忘了替換,編譯時會報出以下錯誤:

[armeabi] Compile thumb : mp3lame <= bitstream.c
In file included from jni/bitstream.c:36:0:
jni/util.h:574:5: error: unknown type name 'ieee754_float32_t'
jni/util.h:574:40: error: unknown type name 'ieee754_float32_t'
make.exe: *** [obj/local/armeabi/objs/mp3lame/bitstream.o] Error 1

編譯庫文件。可能會報出警告,忽略即可。

Lame需要對外提供的方法

  • init 初始化
  • inSamplerate : 輸入采樣頻率 Hz
  • inChannel : 輸入聲道數
  • outSamplerate : 輸出采樣頻率 Hz
  • outBitrate : Encoded bit rate. KHz
  • quality : MP3音頻質量。0~9。 其中0是最好,非常慢,9是最差。

推薦:

2 :near-best quality, not too slow
5 :good quality, fast
7 :ok quality, really fast

private static final int DEFAULT_LAME_MP3_QUALITY = 7;
/**
 * 與DEFAULT_CHANNEL_CONFIG相關,因為是mono單聲,所以是1
 */
private static final int DEFAULT_LAME_IN_CHANNEL = 1;
/**
 * Encoded bit rate. MP3 file will be encoded with bit rate 32kbps 
 */ 
private static final int DEFAULT_LAME_MP3_BIT_RATE = 32;

 

/*
* Initialize lame buffer
* mp3 sampling rate is the same as the recorded pcm sampling rate 
* The bit rate is 32kbps
* 
*/
LameUtil.init(DEFAULT_SAMPLING_RATE, DEFAULT_LAME_IN_CHANNEL, DEFAULT_SAMPLING_RATE, DEFAULT_LAME_MP3_BIT_RATE, DEFAULT_LAME_MP3_QUALITY);

encode

  • bufferLeft : 左聲道數據
  • bufferRight:右聲道數據
  • samples :每個聲道輸入數據大小
  • mp3buf :用于接收轉換后的數據。7200 + (1.25 * buffer_l.length)

這里需要解釋一下:

Task task = mTasks.remove(0);
short[] buffer = task.getData();
int readSize = task.getReadSize();
int encodedSize = LameUtil.encode(buffer, buffer, readSize, mMp3Buffer);
  • 左右聲道 :當前聲道選的是單聲道,因此兩邊傳入一樣的buffer。
  • 輸入數據大小 :錄音線程讀取到buffer中的數據不一定是占滿的,所以read方法會返回當前大小size,即前size個數據是有效的音頻數據,后面的數據是以前留下的廢數據。 這個size同樣需要傳入到Lame編碼器中用于編碼。
  • mp3的buffer:官方規(guī)定了計算公式:7200 + (1.25 * buffer_l.length)。(可以在lame.h文件中看到)

flush

將MP3結尾信息寫入buffer中。
傳入參數:mp3buf至少7200字節(jié)。這里還是用以前定義的mp3buf來傳入,避免創(chuàng)建過多的數組。

close

關閉釋放Lame

OK,到這里,核心的轉換代碼就完成了,我們再來點錦上添花的東西。

音量

一般我們在做錄音的時候,都會有一個需求,根據音量的大小顯示一個動畫,讓錄音顯得更生動一些。
當然,我在這個庫里也提供了。
那么怎么來計算音量呢?
我參考了三星的音量計算。
總結如下:

/**
* 此計算方法來自samsung開發(fā)范例
* 
* @param buffer
* @param readSize
*/
private void calculateRealVolume(short[] buffer, int readSize) {
  int sum = 0;
  for (int i = 0; i < readSize; i++) { 
    sum += buffer[i] * buffer[i]; 
  } 
  if (readSize > 0) {
    double amplitude = sum / readSize;
    mVolume = (int) Math.sqrt(amplitude);
  }
};

關于最大音量

其實對于音量,我不是特別明白。
最大音量在三星的代碼中給出的是4000,但是我在實際的測試中發(fā)現,這個計算公式得出的音量大小一般都在1500以內。
因此在我提供的錄音庫里面,我把最大音量規(guī)定為了2000。
這塊兒歡迎大家來提寶貴意見。

MP3錄音實現參考

yhirano/Mp3VoiceRecorderSampleForAndroid
日本人寫的,感覺他的判斷不完善,有點巧合編程的意思,也或許是我沒看懂。

talzeus/AndroidMp3Recorder
比較嚴謹的代碼。主要依據這個庫進行的修改。

存在的問題:

AudioRecord傳入參數很多沒有按Android規(guī)定傳入。如采樣頻率使用了22050Hz。

使用了自己構造的RingBuffer,看這有點頭暈。 我在庫里使用List來存儲未編碼的音頻數據,更容易理解。

沒有提供音量大小。

相關文章

  • 利用Android中的TextView實現逐字顯示動畫

    利用Android中的TextView實現逐字顯示動畫

    在安卓程序啟動的時候,想逐字顯示一段話,每個字都有一個從透明到不透明的漸變動畫。那如何顯示這個效果,下面一起來看看。
    2016-08-08
  • Android傳感器SensorEventListener之加速度傳感器

    Android傳感器SensorEventListener之加速度傳感器

    今天小編就為大家分享一篇關于Android傳感器SensorEventListener之加速度傳感器,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • Android實現分享微信好友及出現閃退的解決辦法

    Android實現分享微信好友及出現閃退的解決辦法

    這篇文章主要介紹了Android實現分享微信好友及出現閃退的解決辦法的相關資料,需要的朋友可以參考下
    2016-03-03
  • Kotlin集合List Set Map使用介紹詳解

    Kotlin集合List Set Map使用介紹詳解

    集合是可變數量(可能為0)的一組條目,kotlin標準庫提供一個整套用于集合管理的工具,各種集合對于解決問題都具有重要意義,并且經常用到。kotlin中的集合與Java基本類似
    2022-09-09
  • 自定義視圖View繪圖基礎之Path的使用

    自定義視圖View繪圖基礎之Path的使用

    這篇文章主要介紹了自定義視圖View繪圖基礎之Path的使用,path類是一個非常有用的類,他可以預先在view上講N個點連成一條“路徑”,然后調用Canvas的drawPath(path,paint)即可沿著路徑繪制圖形,需要的朋友可以參考下
    2023-04-04
  • Android7.0開發(fā)實現Launcher3去掉應用抽屜的方法詳解

    Android7.0開發(fā)實現Launcher3去掉應用抽屜的方法詳解

    這篇文章主要介紹了Android7.0開發(fā)實現Launcher3去掉應用抽屜的方法,結合實例形式分析了Android7.0 Launcher3調整界面布局的相關操作技巧與注意事項,需要的朋友可以參考下
    2017-11-11
  • Android?手寫RecyclerView實現列表加載

    Android?手寫RecyclerView實現列表加載

    這篇文章主要介紹了Android?手寫RecyclerView實現列表加載,涉及到列表的需求,肯定第一時間想到RecyclerView,即便是自定義View,那么RecyclerView也會是首選,為什么會選擇RecyclerView而不是ListView,主要就是RecyclerView的內存復用機制,這也是RecyclerView的核心?
    2022-08-08
  • Android開發(fā)教程之獲取系統(tǒng)輸入法高度的正確姿勢

    Android開發(fā)教程之獲取系統(tǒng)輸入法高度的正確姿勢

    這篇文章主要給大家介紹了關于Android開發(fā)教程之獲取系統(tǒng)輸入法高度的正確姿勢,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Android具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-10-10
  • Android TextView實現多文本折疊、展開效果

    Android TextView實現多文本折疊、展開效果

    這篇文章主要為大家詳細介紹了Android TextView實現多文本折疊、展開效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • 微信公眾平臺開發(fā)接口PHP SDK完整版

    微信公眾平臺開發(fā)接口PHP SDK完整版

    官方提供的SDK只有一個文本消息功能,我們將所有消息的消息類型及事件響應都整理了進來,并且加入日志記錄
    2014-05-05

最新評論

澄江县| 隆回县| 来安县| 社旗县| 霍邱县| 南通市| 西宁市| 乐都县| 商都县| 自治县| 浦城县| 迭部县| 新丰县| 满洲里市| 富源县| 连城县| 富宁县| 平潭县| 瑞丽市| 湖口县| 定远县| 古蔺县| 图木舒克市| 津市市| 布尔津县| 龙川县| 新龙县| 宜春市| 将乐县| 津市市| 亳州市| 木兰县| 丰台区| 册亨县| 平湖市| 兖州市| 乌海市| 会宁县| 天全县| 张家界市| 新蔡县|