android 音乐 简书,Android音频开发(7):音乐可视化-FFT频谱图
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频谱图相关推荐
- Android音频开发(7):音乐可视化-FFT频谱图
Android 音频开发 目录 Android音频开发(1):音频相关知识 Android音频开发(2):使用AudioRecord录制pcm格式音频 Android音频开发(3):使用AudioRe ...
- android pcm频谱_Android音频开发(7):音乐可视化-FFT频谱图
Android 音频开发 目录 一.演示 image 二.实现 实现流程: 使用MediaPlayer播放传入的音乐,并拿到mediaPlayerId 使用Visualizer类拿到拿到MediaPl ...
- Android入门简书,android ndk开发入门随笔(一)
ndk,jni入门随笔 因为工作缘故最近在研究jni,ndk方面知识,在此总结入坑以来的一些问题. 配置环境可以在下面geogle官方看.下面是链接 我一说ndk,jni可能小伙伴要问了这是什么,在此 ...
- android 购物车 简书,Android仿饿了么购物车效果
先看下效果图: ezgif-1-8f133ca916.gif 1.首先列表布局采用Recycleview android:id="@+id/container" android:l ...
- android matrix 简书,[Android] ImageView ScaleType完全解析
ImageView有一个ScaleType的属性,该属性决定了图片在ImageView上的展现形式,包括:是否进行缩放.如何进行缩放.缩放之后图片的摆放位置等等.官方介绍如下: Options for ...
- android spinner 简书,Android NiceSpinner
NiceSpinner 是 Android 端的一款第三方控件,自带箭头动画效果 真的是简单又好用哦!有需要的小伙伴们可以试试啦. 效果图如下: image 1. 导入模块 在 Github 下载并导 ...
- android dialog 简书,Android自定义Dialog
在开发中,我们经常会用到一些自定义布局的对话框.每次自己写的时候,经常会遇到一些细小的问题需要上网来解决,所以干脆把经常遇到的几个问题总结了一下,这样就可以愉快自定义dialog了.本文主要总结的问题 ...
- android opengl 简书,Android OpenGL入门
如今VR这么火,感觉有必要先把OpenGL学好,为以后转VR奠定一些基础.一年前,接触过Android的OpenGL,当时是实现了在Android上显示标准的3D文件(STL格式).现在打算整理一下O ...
- android room 简书,Android Room 的坑
在添加依赖时,官网给出的是: def room_version = "2.0.0-beta01" implementation "androidx.room:room-r ...
最新文章
- 不一样的Gradle多渠道配置总结
- QComboBox可以随意设置itemData的数值
- vim 代码提示功能,让vim可以媲美IDE
- iPhone5:4G是否进入主流的风向标?
- java基本特性_Java面试总结之Java基础
- 漫步数理统计二十一——变换:随机向量
- c语言 自动化办公,办公自动化快捷键大全
- extjs中grid中行内文本或图片居中显示
- 合并两个有序数组-c语言
- 汇编语言怎么编译python_汇编语言编译器的编写方法
- 你的程序要读入一系列正整数数据,输入-1表示输入结束,-1本身不是输入的数据,程序输出读到的数据中的奇数和偶数的个数。(Java经典编程案例)
- 如何设置谷歌浏览器在新窗口中打开链接
- GSM多时隙NV配置
- springboot驾校报名系统 微信小程序
- 软件设计师教程(九)计算机系统知识-软件工程基础知识
- ping协议(ICMP)的原理
- 【原创】所谓“读心术”的伎俩
- 单机模拟集群(三主两从)
- keep-alive原理
- lg g5 h868 android 7,LG G5 (H868)全网通智能手机