android录制mp3音频,Android MP3录制,波形显示,音频权限兼容与播放
我又來掀桌子了(ノ?益?)ノ彡┻━┻,這次是MP3錄制和顯示聲音波形的一個故事。
先看看效果┑( ̄Д  ̄)┍
2
1
MP3錄制的邏輯過程就忽略了(真的不是因為懶(。??)ノ),這里用的是開源的AndroidMP3Recorder,不過大神太忙了,表示停止維護了,錄制效果還不錯,站在巨人的肩膀上(cao xi),增加了音頻錄制的權限判斷和數據提取的邏輯。關于錄制的請戳這里傳送。
增加權限提示
在音頻相關開發的時候,經常涉及到權限的問題,而6.0以下的時候是沒有接口判斷的,如何敏感的權限,為啥子沒有呢(ノ?益?)ノ彡┻━┻,所以經常會和camera,拍攝視頻一樣,通過try這個crash來判斷吧。
如下在startRecording的時候把crash捕獲了(讓你崩潰(ノ?益?)ノ彡┻━┻),之后通過判斷int readSize = mAudioRecord.read(mPCMBuffer, 0, mBufferSize);讀出來的是,ERROR_INVALID_OPERATION ,ERROR_BAD_VALUE,的話,那么就通知你的頁面:這貨不給權限還想錄音,逗我嗎···然后退出錄制。
···
try {
mAudioRecord.startRecording();
} catch (Exception ex) {
ex.printStackTrace();
}
···
while (mIsRecording) {
int readSize = mAudioRecord.read(mPCMBuffer, 0, mBufferSize);
if (readSize == AudioRecord.ERROR_INVALID_OPERATION ||
readSize == AudioRecord.ERROR_BAD_VALUE) {
if (errorHandler != null && !sendError) {
sendError = true;
errorHandler.sendEmptyMessage(ERROR_TYPE);
mIsRecording = false;
isError = true;
}
} else {
if (readSize > 0) {
mEncodeThread.addTask(mPCMBuffer, readSize);
calculateRealVolume(mPCMBuffer, readSize);
sendData(mPCMBuffer, readSize);
} else {
if (errorHandler != null && !sendError) {
sendError = true;
errorHandler.sendEmptyMessage(ERROR_TYPE);
mIsRecording = false;
isError = true;
}
}
}
}
再采樣(這個詞看起來很高端的樣子)
其實就是音頻數據進行縮減,因為手機每次讀上來的音頻數據是比較大的,這和我們配置的錄制參數也有關系,這里使用的是44100HZ的參數,那么我們直接降頻就好了。(這波裝的可以啊~)
如下面所看到的,其實就一個for循環,首先把音頻數據的長度除了300(哈哈,一下子少了好多),之后每300個里面取出最大和最小值(音頻數據是正負數的),目前我們用的是最大值,這樣把每300個里面的最大值都存起來,就有了一個降頻后的音頻數據列表了。
private void sendData(short[] shorts, int readSize) {
if (dataList != null) {
int length = readSize / 300;
short resultMax = 0, resultMin = 0;
for (short i = 0, k = 0; i < length; i++, k += 300) {
for (short j = k, max = 0, min = 1000; j < k + 300; j++) {
if (shorts[j] > max) {
max = shorts[j];
resultMax = max;
} else if (shorts[j] < min) {
min = shorts[j];
resultMin = min;
}
}
if (dataList.size() > maxSize) {
dataList.remove(0);
}
dataList.add(resultMax);
}
}
}
根據音頻數據繪制波形
1、首先,你得有一個女····呸呸呸,你要有一個View,自定義一個View用來繪制,繪制的邏輯有這么幾個:
獲取View的大小,取的寬度,高度,還有基線。對基♂線。
生成兩張bitmap作為雙緩沖繪制,避免閃爍。(別問我為什么,你用一張試試)
對音頻數據塊的數據大小進行判斷,取的縮放系數,做縮放波形處理。
根據控件寬度,間隔系數,從基線開始繪制波形到bitmap,將這個bitmap繪制到顯示的bitmap2。
通知界面顯示將這個bitmap2 繪制到界面上。
是不是很簡單呢?是啊~確實很簡單( ̄o ̄) .那到這里就結束88咯。
咦,下面還有啊 (ˉ▽ ̄~) 切~~
這里首先通過onVisibilityChanged,在里面注冊了一個addOnPreDrawListener,在addOnPreDrawListener里面獲取到了view的大小、基線,生成兩張需要的bitmap。
@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (visibility == VISIBLE && mBackgroundBitmap == null) {
ViewTreeObserver vto = getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (getWidth() > 0 && getHeight() > 0) {
mWidthSpecSize = getWidth();
mHeightSpecSize = getHeight();
mBaseLine = mHeightSpecSize / 2;
mBackgroundBitmap = Bitmap.createBitmap(mWidthSpecSize, mHeightSpecSize, Bitmap.Config.ARGB_8888);
mBitmap = Bitmap.createBitmap(mWidthSpecSize, mHeightSpecSize, Bitmap.Config.ARGB_8888);
mBackCanVans.setBitmap(mBackgroundBitmap);
mCanvas.setBitmap(mBitmap);
ViewTreeObserver vto = getViewTreeObserver();
vto.removeOnPreDrawListener(this);
}
return true;
}
});
}
}
音頻數據是一塊一塊的list過來的,所以獲取每個塊中最大值,與當前view對比,就知道這一塊數據需要縮放的系數啦。
/**
* 根據當前塊數據來判斷縮放音頻顯示的比例
*
* @param list 音頻數據
*/
private void resolveToWaveData(ArrayList list) {
short allMax = 0;
for (Short sh : list) {
if (sh > allMax) {
allMax = sh;
}
}
int curScale = allMax / mBaseLine;
if (curScale > mScale) {
mScale = ((curScale == 0) ? 1 : curScale);
}
}
開始繪制到bitmap上啦~
首先把bitmap洗干凈drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);,然后繪制基線,接著就是for循環繪制音頻線了。
繪制以基線為軸,每個音頻數據除以縮放系數(需要考慮乘的情況?反正我bu care~),然后繪制上下的豎線,之后把這個bitmap繪制到要完整顯示的mBackgroundBitmap,最后通知界面更新(發到UI線程),然后界面把要顯示的mBackgroundBitmap貼出來。
if (mBackCanVans != null) {
//洗白白
mBackCanVans.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mBackCanVans.drawLine(0, mBaseLine, mWidthSpecSize, mBaseLine, mPaint);
int drawBufsize = dataList.size();
//判斷大小,是否改變顯示的比例
for (int i = 0, j = 0; i < drawBufsize; i++, j += mOffset) {
Short sh = dataList.get(i);
short max = (short) (mBaseLine - sh / mScale);
short min;
if (mWaveCount == 2) {
min = (short) (sh / mScale + mBaseLine);
} else {
min = (short) (mBaseLine);
}
mBackCanVans.drawLine(j, mBaseLine, j, max, mPaint);
mBackCanVans.drawLine(j, min, j, mBaseLine, mPaint);
}
synchronized (mLock) {
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
mCanvas.drawBitmap(mBackgroundBitmap, 0, 0, mPaint);
}
Message msg = new Message();
msg.what = 0;
handler.sendMessage(msg);
}
···
@Override
protected void onDraw(Canvas c) {
super.onDraw(c);
if (mIsDraw && mBitmap != null) {
synchronized (mLock) {
c.drawBitmap(mBitmap, 0, 0, mViewPaint);
}
}
}
音頻播放AudioPlayer
這個比較簡單,就不廢話了,真的不費話了。就是實現了一個內部定時器,不斷的獲取當前位置,誰讓MediaPlayer本來就支持播放網絡和本地,接口又豐富呢,有興趣demo見。
最后~歡迎來個人的Github
一定要來哈
總結
以上是生活随笔為你收集整理的android录制mp3音频,Android MP3录制,波形显示,音频权限兼容与播放的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高会的计算机模块,2017高会《职称计算
- 下一篇: 子网划分及子网掩码计算方法(HCNP)