Android 音频开发 目录

一、演示

image

二、实现

实现流程:

使用MediaPlayer播放传入的音乐,并拿到mediaPlayerId

使用Visualizer类拿到拿到MediaPlayer播放中的音频数据(wave/fft)

将数据用自定义控件展现出来

三、准备工作

使用Visualizer需要录音的动态权限, 如果播放sd卡音频需要STORAGE权限

private static final String[] PERMISSIONS = new String[]{

Manifest.permission.RECORD_AUDIO,

Manifest.permission.MODIFY_AUDIO_SETTINGS

};

ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS, 1);

四、开始播放

private MediaPlayer.OnPreparedListener preparedListener = new

/**

* 播放音频

*

* @param raw 资源文件id

*/

private void doPlay(final int raw) {

try {

mediaPlayer = MediaPlayer.create(MyApp.getInstance(), raw);

if (mediaPlayer == null) {

Logger.e(TAG, "mediaPlayer is null");

return;

}

mediaPlayer.setOnErrorListener(errorListener);

mediaPlayer.setOnPreparedListener(preparedListener);

} catch (Exception e) {

Logger.e(e, TAG, e.getMessage());

}

}

/**

* 获取MediaPlayerId

* 可视化类Visualizer需要此参数

* @return MediaPlayerId

*/

public int getMediaPlayerId() {

return mediaPlayer.getAudioSessionId();

}

五、使用可视化类Visualizer获取当前音频数据

Visualizer 有两个比较重要的参数

设置可视化数据的数据大小 范围[Visualizer.getCaptureSizeRange()[0]~Visualizer.getCaptureSizeRange()[1]]

社会可视化数据的采集频率 范围[0~Visualizer.getMaxCaptureRate()]

OnDataCaptureListener 有2个回调,一个用于显示FFT数据,展示不同频率的振幅,另一个用于显示声音的波形图

private Visualizer.OnDataCaptureListener dataCaptureListener = new Visualizer.OnDataCaptureListener() {

@Override

public void onWaveFormDataCapture(Visualizer visualizer, final byte[] waveform, int samplingRate) {

audioView.post(new Runnable() {

@Override

public void run() {

audioView.setWaveData(waveform);

}

});

}

@Override

public void onFftDataCapture(Visualizer visualizer, final byte[] fft, int samplingRate) {

audioView2.post(new Runnable() {

@Override

public void run() {

audioView2.setWaveData(fft);

}

});

}

};

private void initVisualizer() {

try {

int mediaPlayerId = mediaPlayer.getMediaPlayerId();

if (visualizer != null) {

visualizer.release();

}

visualizer = new Visualizer(mediaPlayerId);

/**

*可视化数据的大小: getCaptureSizeRange()[0]为最小值,getCaptureSizeRange()[1]为最大值

*/

int captureSize = Visualizer.getCaptureSizeRange()[1];

int captureRate = Visualizer.getMaxCaptureRate() * 3 / 4;

visualizer.setCaptureSize(captureSize);

visualizer.setDataCaptureListener(dataCaptureListener, captureRate, true, true);

visualizer.setScalingMode(Visualizer.SCALING_MODE_NORMALIZED);

visualizer.setEnabled(true);

} catch (Exception e) {

Logger.e(TAG, "请检查录音权限");

}

}

波形数据和傅里叶数据的关系请看面这张图:

image

六、编写自定义控件,展示数据

处理数据: visualizer 回调中的数据中是存在负数的,需要转换一下,用于显示

当byte 为 -128时Math.abs(fft[i]) 计算出来的值会越界,需要手动处理一下

byte 的范围: -128~127

/**

* 预处理数据

*

* @return

*/

private static byte[] readyData(byte[] fft) {

byte[] newData = new byte[LUMP_COUNT];

byte abs;

for (int i = 0; i < LUMP_COUNT; i++) {

abs = (byte) Math.abs(fft[i]);

//描述:Math.abs -128时越界

newData[i] = abs < 0 ? 127 : abs;

}

return newData;

}

紧接着就是根据数据去绘制图形

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

wavePath.reset();

for (int i = 0; i < LUMP_COUNT; i++) {

if (waveData == null) {

canvas.drawRect((LUMP_WIDTH + LUMP_SPACE) * i,

LUMP_MAX_HEIGHT - LUMP_MIN_HEIGHT,

(LUMP_WIDTH + LUMP_SPACE) * i + LUMP_WIDTH,

LUMP_MAX_HEIGHT,

lumpPaint);

continue;

}

switch (upShowStyle) {

case STYLE_HOLLOW_LUMP:

drawLump(canvas, i, false);

break;

case STYLE_WAVE:

drawWave(canvas, i, false);

break;

default:

break;

}

switch (downShowStyle) {

case STYLE_HOLLOW_LUMP:

drawLump(canvas, i, true);

break;

case STYLE_WAVE:

drawWave(canvas, i, true);

break;

default:

break;

}

}

}

/**

* 绘制矩形条

*/

private void drawLump(Canvas canvas, int i, boolean reversal) {

int minus = reversal ? -1 : 1;

if (waveData[i] < 0) {

Logger.w("waveData", "waveData[i] < 0 data: %s", waveData[i]);

}

float top = (LUMP_MAX_HEIGHT - (LUMP_MIN_HEIGHT + waveData[i] * SCALE) * minus);

canvas.drawRect(LUMP_SIZE * i,

top,

LUMP_SIZE * i + LUMP_WIDTH,

LUMP_MAX_HEIGHT,

lumpPaint);

}

/**

* 绘制曲线

* 这里使用贝塞尔曲线来绘制

*/

private void drawWave(Canvas canvas, int i, boolean reversal) {

if (pointList == null || pointList.size() < 2) {

return;

}

float ratio = SCALE * (reversal ? -1 : 1);

if (i < pointList.size() - 2) {

Point point = pointList.get(i);

Point nextPoint = pointList.get(i + 1);

int midX = (point.x + nextPoint.x) >> 1;

if (i == 0) {

wavePath.moveTo(point.x, LUMP_MAX_HEIGHT - point.y * ratio);

}

wavePath.cubicTo(midX, LUMP_MAX_HEIGHT - point.y * ratio,

midX, LUMP_MAX_HEIGHT - nextPoint.y * ratio,

nextPoint.x, LUMP_MAX_HEIGHT - nextPoint.y * ratio);

canvas.drawPath(wavePath, lumpPaint);

}

}

android 音乐 简书,Android音频开发(7):音乐可视化-FFT频谱图相关推荐

  1. Android音频开发(7):音乐可视化-FFT频谱图

    Android 音频开发 目录 Android音频开发(1):音频相关知识 Android音频开发(2):使用AudioRecord录制pcm格式音频 Android音频开发(3):使用AudioRe ...

  2. android pcm频谱_Android音频开发(7):音乐可视化-FFT频谱图

    Android 音频开发 目录 一.演示 image 二.实现 实现流程: 使用MediaPlayer播放传入的音乐,并拿到mediaPlayerId 使用Visualizer类拿到拿到MediaPl ...

  3. Android入门简书,android ndk开发入门随笔(一)

    ndk,jni入门随笔 因为工作缘故最近在研究jni,ndk方面知识,在此总结入坑以来的一些问题. 配置环境可以在下面geogle官方看.下面是链接 我一说ndk,jni可能小伙伴要问了这是什么,在此 ...

  4. android 购物车 简书,Android仿饿了么购物车效果

    先看下效果图: ezgif-1-8f133ca916.gif 1.首先列表布局采用Recycleview android:id="@+id/container" android:l ...

  5. android matrix 简书,[Android] ImageView ScaleType完全解析

    ImageView有一个ScaleType的属性,该属性决定了图片在ImageView上的展现形式,包括:是否进行缩放.如何进行缩放.缩放之后图片的摆放位置等等.官方介绍如下: Options for ...

  6. android spinner 简书,Android NiceSpinner

    NiceSpinner 是 Android 端的一款第三方控件,自带箭头动画效果 真的是简单又好用哦!有需要的小伙伴们可以试试啦. 效果图如下: image 1. 导入模块 在 Github 下载并导 ...

  7. android dialog 简书,Android自定义Dialog

    在开发中,我们经常会用到一些自定义布局的对话框.每次自己写的时候,经常会遇到一些细小的问题需要上网来解决,所以干脆把经常遇到的几个问题总结了一下,这样就可以愉快自定义dialog了.本文主要总结的问题 ...

  8. android opengl 简书,Android OpenGL入门

    如今VR这么火,感觉有必要先把OpenGL学好,为以后转VR奠定一些基础.一年前,接触过Android的OpenGL,当时是实现了在Android上显示标准的3D文件(STL格式).现在打算整理一下O ...

  9. android room 简书,Android Room 的坑

    在添加依赖时,官网给出的是: def room_version = "2.0.0-beta01" implementation "androidx.room:room-r ...

最新文章

  1. 不一样的Gradle多渠道配置总结
  2. QComboBox可以随意设置itemData的数值
  3. vim 代码提示功能,让vim可以媲美IDE
  4. iPhone5:4G是否进入主流的风向标?
  5. java基本特性_Java面试总结之Java基础
  6. 漫步数理统计二十一——变换:随机向量
  7. c语言 自动化办公,办公自动化快捷键大全
  8. extjs中grid中行内文本或图片居中显示
  9. 合并两个有序数组-c语言
  10. 汇编语言怎么编译python_汇编语言编译器的编写方法
  11. 你的程序要读入一系列正整数数据,输入-1表示输入结束,-1本身不是输入的数据,程序输出读到的数据中的奇数和偶数的个数。(Java经典编程案例)
  12. 如何设置谷歌浏览器在新窗口中打开链接
  13. GSM多时隙NV配置
  14. springboot驾校报名系统 微信小程序
  15. 软件设计师教程(九)计算机系统知识-软件工程基础知识
  16. ping协议(ICMP)的原理
  17. 【原创】所谓“读心术”的伎俩
  18. 单机模拟集群(三主两从)
  19. keep-alive原理
  20. lg g5 h868 android 7,LG G5 (H868)全网通智能手机

热门文章

  1. 蓝汛ChinaCache打破传输瓶颈,提高宽带利用率
  2. BAT云平台之争:百度领先,阿里发力,腾讯落后
  3. Armadillo使用介绍(八):第二个Armadillo程序
  4. box-shadow的多种用法
  5. NS32F103C8T6 可完美替代 STM32F103C8T6
  6. 【快速入门大数据】Hadoop项目实战-用户行为日志
  7. 优锘科技:可视化加速“历史数据库”运转
  8. Unity学习日志_Unity地形系统简介
  9. 题目目录:java练习题
  10. 1 天卖 1.96 亿,知识付费浪潮下的喜马拉雅是如何做增长的