我又来掀桌子了(ノಠ益ಠ)ノ彡┻━┻,这次是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录制,波形显示,音频权限兼容与播放相关推荐

  1. android mp3数据格式,Android录制mp3格式文件

    前言 最近做一个即时通信类的项目,由于要保证pc端,iOS端和Android端的通用性,最终统一为MP3格式,一直担心MP3格式会不会很大,但是实测还是可以接受的.下面来看看具体步骤: 工具 MP3格 ...

  2. android 语音发送功能,Android仿微信、录制音频并发送功能

    MyRecorder(仿微信,录制音频并发送功能) ①布局实现(activity_main.xml)布局采用线性布局,上面使用的一个ListView,下面使用的是一个自定义的Button(会在下面进行 ...

  3. 我的Android进阶之旅------Android MediaPlayer播放网络音频的实例--网络mp3播放器

    上一篇写了个简单的MP3播放器 ,这次写一个可以播放网络音频资源的播放器 本实例可以实现音乐播放器除了来电的时候会暂停播放,通话结束后恢复播放外,打开其他的Activity都可以继续播放音乐,享受一边 ...

  4. android播放mp3方法,Android MediaPlayer 播放音频的方式

    主要介绍使用MediaPlayer播放音频的方式.关于MediaPlayer的基础知识,比如状态,可以参考Android MediaPlayer基础简介. 为了方便表达,定义变量名为mediaPlay ...

  5. mediaplayer android mp3 url,Android MediaPlayer 播放音频

    主要介绍使用MediaPlayer播放音频的方式.关于MediaPlayer的基础知识,比如状态,可以参考Android MediaPlayer 基础简介 为了方便表达,定义变量名为mediaPlay ...

  6. android 使用MediaCodec(根据设备状况硬编解码)来转码音频(MP3 to aac),并同时裁剪音频

    第一次写这样的博文,写得不好,望大家及时指出博文中的错误,以便于相互学习. 本来主要介绍如何使用mediacodec来转码音频,同时实现音频的裁剪.对于mediacodec相信大家都不陌生,没做过也听 ...

  7. android 从服务器获取音频,Android项目之旅三 简易Mp3播放器从获取服务器端Mp3信息...

    上面我们已经把页面弄好了,下面我们就要在里面实现我们的功能了哦,今天主要是在RemoteMp3Activity中做功夫,因为项目启动后,这个Activity要做很多事情哦: 1)从服务器上面下载mp3 ...

  8. 【Android FFMPEG 开发】Android 中使用 FFMPEG 将 PCM 音频采样转为 MP3 格式

    文章目录 一.前置操作 ( 移植 FFMPEG ) 二.FFMPEG 将 PCM 采样转为 MP3 格式的命令 三.Android FFMPEG 混音源代码完整示例 四.博客源码 一.前置操作 ( 移 ...

  9. android mp3 编码,Android 如何采用Lame编码器编码mp3文件

    这篇文章会基于下面3个问题来展开讲解. 1.什么是Lame? 2.为什么采用Lame? 3.Lame在Android应用上如何使用? 一.什么是Lame LAME is a high quality ...

最新文章

  1. python如何调用cpp文件的接口函数_C++中嵌入Python调用
  2. C++/C的文件结构
  3. linux pid t 头文件_Linux信号处理
  4. python在线运行-Python在线练习运行平台
  5. 程序员修神之路--容器技术为什么会这么流行(记得去抽奖)
  6. 010 Editor v8.0.1(32 - bit) 算法逆向分析、注册机编写
  7. 【图论】Prim算法求最小生成树详解
  8. win10计算机磁盘图标,win10系统本地磁盘图标显示异常如何恢复
  9. 软件测试cpu性能测试案例,cpu测试(cpu性能测试软件)
  10. Virtualbox主机与虚拟机相互访问
  11. Unity3d 周分享(22期 2019.8.30 )
  12. skyeye与uClinux的安装
  13. windows 取消开机自检
  14. 读书百客:《送客之江西》赏析
  15. 北京大学肖臻老师《区块链技术与应用》公开课笔记8——BTC挖矿篇
  16. 间歇性禁食 肠道菌群 心血管代谢疾病
  17. 区块链安全和隐私问题有点与众不同
  18. thinkphp配置文件设置session有效期未生效的问题
  19. 《人工智能》之《机器学习》
  20. 陈延伟:任督二脉之内存管理总结笔记

热门文章

  1. ntp server 配置参数_NTP配置-debian_chen-ChinaUnix博客
  2. 基于用户的协同过滤(user-based collaborative filtering)
  3. 【文学】平凡的世界第一部
  4. html语言的单词,如何匹配一段html中的单词或者词组
  5. 计算机原理与设计verilog pdf,计算机原理与设计: Verilog HDL版
  6. Android App发布到应用市场的流程
  7. grafana的实时地图(学习笔记)
  8. RoundedImageView 处理图片存在毛边的问题
  9. ubuntu server 18.04 first config
  10. 微信小程序上传文件(图片/word等)功能)