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 pcm频谱_Android音频开发(7):音乐可视化-FFT频谱图相关推荐

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

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

  2. android 音乐 简书,Android音频开发(7):音乐可视化-FFT频谱图

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

  3. android pcm 音量_Android中实时获取音量分贝值详解

    基础知识 度量声音强度,大家最熟悉的单位就是分贝(decibel,缩写为dB).这是一个无纲量的相对单位,计算公式如下: 分子是测量值的声压,分母是参考值的声压(20微帕,人类所能听到的最小声压).因 ...

  4. android pcm频谱_Android音频可视化

    本文作者:熊鋆洋 (网易云音乐大前端团队) 前言 音频可视化,顾名思义就是将声音以视觉的方式呈现出来.如何将音频信号绘制出来?如何将声音的变化在视觉上清晰的表现出来,让视觉和听觉上的感受一致?这些在 ...

  5. android pcm文件大小_Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件...

    一.AudioRecord API详解 AudioRecord是Android系统提供的用于实现录音的功能类. 要想了解这个类的具体的说明和用法,我们可以去看一下官方的文档: AndioRecord类 ...

  6. android 自定义频谱,android – 如何从实时音频开发频谱分析仪?

    我正在开发一个应用程序,实时从麦克风获取源音频,没有文件存储.基本上,我使用: mRecorder = new MediaRecorder(); mRecorder.setAudioSource(Me ...

  7. android开发歌词滑动效果_Android应用开发--MP3音乐播放器滚动歌词实现

    [android]代码库2013年6月2日 简.美音乐播放器开发记录 -----主题 这篇博客的主题是:"滚动歌词的实现" 要的效果如下: ----实现过程 1. 建立歌词内容实体 ...

  8. android pcm文件大小_Android中的PCM设备

    Android上的应用一般都是通过AudioTrack类来播放音频,通过AudioRecord类来录制音频.AudioTrack类和AudioRecord类是Android Frameworks封装提 ...

  9. android cmake 打印_Android NDK 开发:CMake 使用

    1. 前言 当在做 Android NDK 开发时,如果不熟悉用 CMake 来构建,读不懂 CMakeLists.txt 的配置脚本,很容易就会踩坑,遇到编译失败,一个很小的配置问题都会浪费很多时间 ...

最新文章

  1. [05]EXTJS4.0的读写器reader,writer
  2. 一篇叫做决心书的文章
  3. openapi开放平台架构_适用于所有人的MicroProfile OpenAPI
  4. 机器视觉与深度神经网络—洗去浮华,一窥珠玑
  5. linux xia 安装程序,linux更新或安装libzip
  6. Imation亏损额急剧增长 CEO仍表示“成功”
  7. 机器学习实战的P264中代码对应的公式推导
  8. 查看linux的用户
  9. Workflow WF Reference Links for 2009-03-20
  10. 查看mysql错误日志定位mysql错误
  11. (转)Nutz | Nutz项目整合Spring实战
  12. braft的LogEntry日志记录存储LogStorage
  13. 计算机专业的大专大学规划,大学生计算机专业职业规划个人简历
  14. 香港十大外汇交易平台排名(2021最新版)
  15. 走进波分 -- 03.光纤传输系统关键参数
  16. 写给那些让我糊里糊涂的HTTP、TCP、UDP、Socket
  17. 华三交换机基本配置命令
  18. 笔记本电脑屏幕亮度无法调节
  19. 深度学习研究生常用网站||图像处理||医学影像||使用评价||汇总
  20. Web前端开发技术课程大作业——南京旅游景点介绍网页代码html+css+javascript

热门文章

  1. 长文丨亚马逊帝国的人工智能革命史
  2. 如何复制出计算机缓存中的歌曲,怎样提取电脑缓存中的文件 例如音乐
  3. LwIP常见问题FAQ
  4. 学习Excel的使用
  5. javascript 的七种继承方式(三)组合继承
  6. 寻找最大公约数c语言,C语言程序设计100例之(10):最大公约数
  7. python经典面试题100例 面试宝典
  8. html表格去除网格线_HTML表格模式:数据网格
  9. 域名投资市场双十一惊现新宠,ltd域名的火爆让域名服务商慌了手脚
  10. MSSQL Server 2008中的MERGE(不仅仅是合并)