600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > android pcm频谱_Android音频开发(7):音乐可视化-FFT频谱图

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

时间:2022-12-20 00:55:15

相关推荐

android pcm频谱_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);

}

}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。