android實現之高清音訊錄製編碼
場景說明:
在現在有安卓手機中AudioRecord錄製出的音訊是通過揚聲器或表克風錄製出來的。對於外錄即揚聲器錄製的視訊而言,音質十分渣。對於想要錄製現場的聲音如開會時,兩人對話時聲音證據儲存的聲音錄製,完全不能滿足需求。鑑於此,研究安卓平臺下高清音訊錄製的解決方案。
原理:本人實現原理很簡單,分為兩部分:1, 原始音訊採集,2 音訊編碼 。
1,原始音訊採集:
還是通過安卓的API AudioRecord 去採集聲音,這個API錄製聲音不行,但在安卓上採集音訊通常只能通過這個API,因為它直接封裝了底層audio_device裝置。
想要自己另外實現一套採集手段,不大實現且費力。我們可以通過設定一定的音訊取樣率,通道型別,位元速率,緩衝區大小來初始化AudioRecord,讓它來為我們
採集原始音訊,即 PCM 格式音訊。
2 , 音訊轉碼:
採集到PCM音訊後,將PCM格式轉碼為MP3格式音訊。這裡我用的是LAME編碼器,它是一款出色的音訊編碼器,摘自網上的一段介紹,LAME(mitiok.ma.cx)編碼出來的MP3音色純厚、空間寬廣、低音清晰、細節表現良好,它獨創的心理音響模型技術保證了CD音訊還原的真實性。也不知是真是假。呵呵,反正本人用的還不錯,保留了MP3音訊的高清質量。
具體實現步驟:
1,音訊採集:
這裡使用設定的取樣率,位元速率,緩衝區大小初始化AudioRecord
並且設制處理音訊的間隔時間及回撥監聽。
/**
* Initialize audio recorder
*/
private void initAudioRecorder() throws IOException {
int bytesPerFrame = audioFormat.getBytesPerFrame();
/* Get number of samples. Calculate the buffer size (round up to the
factor of given frame size) */
int frameSize = AudioRecord.getMinBufferSize(samplingRate,
channelConfig, audioFormat.getAudioFormat()) / bytesPerFrame;
if (frameSize % FRAME_COUNT != 0) {
frameSize = frameSize + (FRAME_COUNT - frameSize % FRAME_COUNT);
Log.d(TAG, "Frame size: " + frameSize);
}
bufferSize = frameSize * bytesPerFrame;
/* Setup audio recorder */
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
samplingRate, channelConfig, audioFormat.getAudioFormat(),
bufferSize);
// Setup RingBuffer. Currently is 10 times size of hardware buffer
// Initialize buffer to hold data
ringBuffer = new RingBuffer(10 * bufferSize);
buffer = new byte[bufferSize];
// Initialize lame buffer
// mp3 sampling rate is the same as the recorded pcm sampling rate
// The bit rate is 32kbps
RecordLib.init(samplingRate, 1, samplingRate, BIT_RATE);
// Initialize the place to put mp3 file
// String externalPath = Environment.getExternalStorageDirectory()
// .getAbsolutePath();
// File directory = new File(externalPath + "/" + "AudioRecorder");
// if (!directory.exists()) {
// directory.mkdirs();
// Log.d(TAG, "Created directory");
// }
mp3File = new File(mFilePath);
os = new FileOutputStream(mp3File);
// Create and run thread used to encode data
// The thread will
encodeThread = new DataEncodeThread(ringBuffer, os, bufferSize);
encodeThread.start();
audioRecord.setRecordPositionUpdateListener(encodeThread, encodeThread.getHandler());
audioRecord.setPositionNotificationPeriod(FRAME_COUNT);
threads = new AcquireAudioPower();
}
2,音訊轉碼
通過AudioRecord的回撥函式來進行實時音訊轉碼
@Override
public void onPeriodicNotification(AudioRecord recorder) {
processData();
}
/**
* Get data from ring buffer
* Encode it to mp3 frames using lame encoder
* @return Number of bytes read from ring buffer
* 0 in case there is no data left
*/
private int processData() {
int bytes = ringBuffer.read(buffer, bufferSize);
//Log.d(TAG, "Read size: " + bytes);
if (bytes > 0) {
short[] innerBuf = new short[bytes / 2];
ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(innerBuf);
int encodedSize = RecordLib.encode(innerBuf, innerBuf, bytes / 2, mp3Buffer);
if (encodedSize < 0) {
Log.e(TAG, "Lame encoded size: " + encodedSize);
}
try {
os.write(mp3Buffer, 0, encodedSize);
} catch (IOException e) {
Log.e(TAG, "Unable to write to file");
}
return bytes;
}
return 0;
}
3,lame編碼技術實現:
JNIEXPORT void JNICALL Java_com_cunnar_lame_RecordLib_init(
JNIEnv *env, jclass cls, jint inSamplerate, jint outChannel,
jint outSamplerate, jint outBitrate, jint quality) {
if (glf != NULL) {
lame_close(glf);
glf = NULL;
}
glf = lame_init();
lame_set_in_samplerate(glf, inSamplerate);
lame_set_num_channels(glf, outChannel);
lame_set_out_samplerate(glf, outSamplerate);
lame_set_brate(glf, outBitrate);
lame_set_quality(glf, quality);
lame_init_params(glf);
}
JNIEXPORT jint JNICALL Java_com_cunnar_lame_RecordLib_encode(
JNIEnv *env, jclass cls, jshortArray buffer_l, jshortArray buffer_r,
jint samples, jbyteArray mp3buf) {
jshort* j_buffer_l = (*env)->GetShortArrayElements(env, buffer_l, NULL);
jshort* j_buffer_r = (*env)->GetShortArrayElements(env, buffer_r, NULL);
const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf);
jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL);
int result = lame_encode_buffer(glf, j_buffer_l, j_buffer_r,
samples, j_mp3buf, mp3buf_size);
(*env)->ReleaseShortArrayElements(env, buffer_l, j_buffer_l, 0);
(*env)->ReleaseShortArrayElements(env, buffer_r, j_buffer_r, 0);
(*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0);
return result;
}
JNIEXPORT jint JNICALL Java_com_cunnar_lame_RecordLib_flush(
JNIEnv *env, jclass cls, jbyteArray mp3buf) {
const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf);
jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL);
int result = lame_encode_flush(glf, j_mp3buf, mp3buf_size);
(*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0);
return result;
}
JNIEXPORT void JNICALL Java_com_cunnar_lame_RecordLib_close(
JNIEnv *env, jclass cls) {
lame_close(glf);
glf = NULL;
}
總結:將安卓採集到的原始音訊做一次到MP3格式的編碼,即可達到高清錄製的效果。這裡可以再進行聲音變聲的效果,有興趣的可以自己研究一下。
一般都是通過控制採集率,位元速率來實現變聲的。
下面附上本人的實現原始碼。下載原始碼
相關推薦
android實現之高清音訊錄製編碼
場景說明: 在現在有安卓手機中AudioRecord錄製出的音訊是通過揚聲器或表克風錄製出來的。對於外錄即揚聲器錄製的視訊而言,音質十分渣。對於想要錄製現場的聲音如開會時,兩人對話時聲音證據儲存的聲音錄製,完全不能滿足需求。鑑於此,研究安卓平臺下高清音訊錄製的解決方案。
Android Studio之高德地圖實現定位和3D地圖顯示
tor uil track width 博客 5.0 eight ext wid 在應用開發中,地圖開發是常常須要使用的“組件”,國內比較出名的是就是百度地圖和高德地
Android 小樣之高仿淘寶時間軸物流資訊
最近做訂單系統,用到時間軸資訊,首先想到的是淘寶的物流時間軸(網購狗)。不多廢話,首先來看看淘寶物流資訊的樣式 這裡給出兩種解決方法: 使用LinearLayout動態新增view生成物流資訊
Android實現圖片 高斯模糊,以及圖片映象 翻轉。
好久沒寫部落格,發現不止手癢,,原來不學習還是會頹廢的….. 哎….. 速速找了網上比較感興趣的功能,,看著前人大神門的方法實現,方便自己也方便別人: 上圖: 程式碼: MainActivity.class package com.hero.
Android開發之高亮引導
看下圖,今天的任務就是它了,app 的高亮引導的實現,找到幾個github上面已經實現的庫,下載下來原始碼對比分析實現原理,整理自己的知識體系。下面是其中一個的效果圖(我用DialogFragment實
Android 實現圖片高斯模糊演算法,真正有效的工具類
import android.graphics.Bitmap; import android.graphics.Color; public class FastBlur { /*** * 高斯模糊演算法 * @param bmp 要處理的影象
android開發之高仿中國建設銀行App
皇天不負有心人,今天終於被我找到了這篇神文!關於高仿中國建設銀行App的一篇Blog,於是我就不自覺的把它消化成了我的東西了,嘿嘿!不過我是有節操滴,在本文的最後我貼上了此文轉載於哪裡?也希望各位在以後的學習道路上,不要做忘恩負義的人! 各位,準備好了嗎?讓我們一起來看看大
Android實現快速高斯模糊
高斯模糊想必大家都聽說過,百度百科對於高斯模糊的解釋為: 高斯模糊(英語:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等影象處理軟體中廣泛使用的處理效果,通常用它來減少影象噪聲以及降低細節層次。所謂"模糊",
Android短視訊中如何實現720P磨皮美顏錄製?
視訊中磨皮、美顏功能已成為剛需,那麼如何在Android短視訊中實現720P磨皮美顏錄製?本篇文章中,網易雲信資深開發工程師將向大家介紹具體的操作方法。 相關閱讀推薦 《短視訊技術詳解:Android端的短視訊開發技術》 《如何快速實現移動端短視訊功能?》 在And
【android學習筆記】activity間的通訊案例之高德地圖實現天氣查詢
【概述】app實現天氣查詢是再正常不過的功能了,又因為往往不止一個activity去獲取資料,那就想到封裝一個類,需要時去呼叫獲取即可。 【注】因為看文件還有點懵,故將自己抓腦寫的程式碼記錄下,以便查詢 【思路】activity傳送請求--獲取地址--根據地址獲取天氣
Android之高仿騰訊微博
匯入原始碼到eclipse出現報錯的童鞋注意:我用的是utf-8編碼,因為騰訊的api是utf-8編碼,其實我也不喜歡改來該去的! 國慶後,一直在忙,都沒有什麼時間做自己的事情,哎,上班的孩子傷不起啊!這個微博也是斷斷續續的,每天晚
Android 實現高仿iOS桌面效果之可拖動的GridView(上)
最近專案中遇到一個LIstview的拖動效果,github上一搜發現有叫DragListview的開源專案,然後自己再小手一搜拖動排序的GridView,卻沒發現什麼很全很好的開源專案,後
Android開發之通過藍芽耳機實現訊飛語音識別的功能
近階段在開發一款app,實現通過藍芽耳機進行訊飛語音識別,獲取識別結果之後再通過語音合成從藍芽耳機播報出識別結果。上網也查了很多資料,大多是說通過一下兩行程式碼: mAudioManager.setBluetoothScoOn(true);
Android筆記之(圖片高斯+Glide實現微信圖片載入策略+仿微信進度條)
很久以前就想自己實現一下仿微信圖片載入的那種策略了,先載入一張模糊的圖片,然後再載入清晰大圖,今天研究了一下,不過要是Glide支援進度條顯示就好了,不得不說Glide很強大, 不囉嗦了,直接上程式碼了。 首先看看高斯模糊到底怎麼實現,你問我我也不會(^__
Android開發之布局文件裏實現OnClick事件關聯處理方法
intent dsm nbsp ext 關聯 you vertica findview 時間 一般監聽OnClickListener事件,我們都是通過Button button = (Button)findViewById(....); button.se
Keepalive 之 高可用實現
fwmark lvs nginx keepalive1 概述本文將介紹三個Keepalive高可用的實現案例,分別是keepalive實現LVS高可用,keepalive通過fwmark實現LVS高可用,keepalive實現Nginx高可用。2 實驗準備.(1) 各節點時間必須同步,這個操作很關鍵。
2017.12.18 Android開發之消息隊列(實現子線程修改UI組件)
nds ace text read exce xtend prot ktr sta 1.界面布局,以及組件初始化: 組件初始化: private Button button; private Handler handler; @Ove
實現redis高可用主從之sentinel
redis sentinel redis主從 redis高可用 sentinel作用 監控(Monitoring): Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常。 提醒(Notification): 當被監控的某個 Redis 服務器出現問題時, Sentinel 可以
Android面試之HashMap的實現原理
amp 安全 itl 轉載 提高效率 基礎上 ash cti data- 1、HashMap與HashTable的區別 HashMap允許key和value為null; HashMap是非同步的,線程不安全,也可以通過Collections.synchro
基於nginx+swoole+phalcon+atlas實現的高性能負載均衡集群系列之【構建篇】
p12 adb 列表 服務器性能 nodeps devel unit tcl aio 一、簡介 php一直詬病於性能,可對開發者如此友好的語言為什麽不能登上大雅之堂? 於是php一線開發者站了出來。 先有鳥哥優化php引擎,又有rango大神開源swoole。至此,基