通过Android录音进行简单音频分析
Android录音有MediaRecorder和AudioRecord两种方式,前者使用方便,可以直接生成录音文件,但是录音格式为aac和amr等等,都经过压缩处理,不方便进行音频分析。
而用AudioRecord可以得到PCM编码的原音频数据,可以用FFT对数据进行处理,简单分析声音的频率。
1.AndroidRecord录音
private static final String FILE_NAME = "MainMicRecord";
private static final int SAMPLE_RATE = 44100;//Hz,采样频率
private static final double FREQUENCY = 500; //Hz,标准频率(这里分析的是500Hz)
private static final double RESOLUTION = 10; //Hz,误差
private static final long RECORD_TIME = 2000;
private File mSampleFile;
private int bufferSize=0;
private AudioRecord mAudioRecord; private void startRecord() {
try {
mSampleFile = new File(getFilesDir()+"/"+FILE_NAME);
if(mSampleFile.exists()){
if(!mSampleFile.delete()){
return;
}
}
if(!mSampleFile.createNewFile()){
return;
}
} catch(IOException e) {
return;
}
//为了方便,这里只录制单声道
//如果是双声道,得到的数据是一左一右,注意数据的保存和处理
bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,SAMPLE_RATE,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize);
mAudioRecord.startRecording();
new Thread(new AudioRecordThread()).start();
} private class AudioRecordThread implements Runnable{
@Override
public void run() {
//将录音数据写入文件
short[] audiodata = new short[bufferSize/2];
DataOutputStream fos = null;
try {
fos = new DataOutputStream( new FileOutputStream(mSampleFile));
int readSize;
while (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING){
readSize = mAudioRecord.read(audiodata,0,audiodata.length);
if(AudioRecord.ERROR_INVALID_OPERATION != readSize){
for(int i = 0;i<readSize;i++){
fos.writeShort(audiodata[i]);
fos.flush();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
} //在这里release
mAudioRecord.release();
mAudioRecord = null;
}
}
}; //在这里stop的时候先不要release
private void stopRecording() {
mAudioRecord.stop();
} //对录音文件进行分析
private void frequencyAnalyse(){
if(mSampleFile == null){return;
}
try {
DataInputStream inputStream = new DataInputStream(new FileInputStream(mSampleFile));
//16bit采样,因此用short[]
//如果是8bit采样,这里直接用byte[]
//从文件中读出一段数据,这里长度是SAMPLE_RATE,也就是1s采样的数据
short[] buffer=new short[SAMPLE_RATE];
for(int i = 0;i<buffer.length;i++){
buffer[i] = inputStream.readShort();
}
short[] data = new short[FFT.FFT_N]; //为了数据稳定,在这里FFT分析只取最后的FFT_N个数据
System.arraycopy(buffer, buffer.length - FFT.FFT_N,
data, 0, FFT.FFT_N); //FFT分析得到频率
double frequence = FFT.GetFrequency(data);
if(Math.abs(frequence - FREQUENCY)<RESOLUTION){
//测试通过
}else{
//测试失败
}
} catch (IOException e) {
e.printStackTrace();
}
}
2.FFT实现
参考:http://introcs.cs.princeton.edu/java/97data/FFT.java.html
(1)复数类
public class Complex { public double real, imag; public Complex(double real,double im){
this.real = real;
this.imag = im;
} public Complex(){
this(0,0);
} public Complex(Complex c){
this(c.real,c.imag);
} @Override
public String toString() {
return "("+this.real+"+"+this.imag +"i)";
} //加法
public final Complex add(Complex c){
return new Complex(this.real+c.real,this.imag +c.imag);
} //减法
public final Complex minus(Complex c){
return new Complex(this.real-c.real,this.imag -c.imag);
} //求模值
public final double getMod(){
return Math.sqrt(this.real * this.real+this.imag * this.imag);
} //乘法
public final Complex multiply(Complex c){
return new Complex(
this.real*c.real - this.imag *c.imag,
this.real*c.imag + this.imag *c.real);
}
}
(2)FFT求最大频率
public class FFT {
public static final int FFT_N = 4096;
public static final int SAMPLE_RATE = 44100; //HZ //快速傅里叶变换
public static Complex[] getFFT(Complex[] data){
int N = data.length;
if(N==1){
return new Complex[]{data[0]};
}
if(N%2 != 0){
throw new RuntimeException("N is not a power of 2");
} //fft of even/odd terms
Complex[] even = new Complex[N/2];
Complex[] odd = new Complex[N/2];
for(int k = 0;k<N/2;k++){
even[k] = data[2*k];
odd[k] = data[2*k+1];
}
Complex[] q= getFFT(even);
Complex[] r = getFFT(odd); Complex[] y = new Complex[N];
for (int k = 0;k<N/2;k++){
double kth = -2*k*Math.PI/N;
Complex wk = new Complex(Math.cos(kth), Math.sin(kth));
y[k] = q[k].add(wk.multiply(r[k]));
y[k+N/2] = q[k].minus(wk.multiply(r[k]));
} return y;
} //================================================================
public static double GetFrequency(short[] data){
Log.i("FFT","GetFrequency");
if(data.length<FFT_N){
throw new RuntimeException("Data length lower than "+FFT_N);
}
Complex[] f = new Complex[FFT_N];
for(int i=0;i<FFT_N;i++){
f[i] = new Complex(data[i],0); //实部为正弦波FFT_N点采样,赋值为1
//虚部为0
} f = getFFT(f); //进行快速福利叶变换
// String str = "";
// for(int i = 0;i<FFT_N;i++){
// str+=f[i].toString()+" ";
// }
// Log.i("FFT","fft: "+str);
double[] s = new double[FFT_N/2];
// str = "";
for(int i=0;i<FFT_N/2;i++){
s[i] = f[i].getMod();
// str += ""+s[i]+" ";
}
// Log.i("FFT","s: "+str); int fmax=0;
for(int i=1;i<FFT_N/2;i++){ //利用FFT的对称性,只取前一半进行处理
if(s[i]>s[fmax])
fmax=i; //计算最大频率的序号值
}
// Log.i("FFT","max index:"+fmax+" fft:"+f[fmax]+" s:"+s[fmax]);
double fre = fmax*(double)SAMPLE_RATE / FFT_N;
Log.i("FFT","fre:"+fre);
return fre;
}
}
通过Android录音进行简单音频分析的更多相关文章
- Android.mk文件简单分析
Android.mk文件简单分析 一个Android.mk文件用来向编译系统描写叙述须要编译的源码.详细来说:该文件是GNUMakefile的一小部分.会被编译系统解析一次或多次. 能够在每个Andr ...
- Android智能手机上的音频浅析
手机可以说是现在人日常生活中最离不开的电子设备了.它自诞生以来,从模拟的发展到数字的,从1G发展到目前的4G以及不久将来的5G,从最初的只有唯一的功能(打电话)发展到目前的全功能,从功能机(featu ...
- Android 录音和播放
今天工作上需要做一个一边录音一边播放的功能,大致原因是有一个外部设备输入音频到我们机器,然后我们机器需要马上把音频播放出来.所以了解了一些有关录音和播放的知识.接到这个任务的第一反应就是看看Andro ...
- Android智能手机上的音频浅析【转】
本文转载自:https://blog.csdn.net/david_tym/article/details/80903385 手机可以说是现在人日常生活中最离不开的电子设备了.它自诞生以来,从模拟的发 ...
- (转)Android 系统 root 破解原理分析
现在Android系统的root破解基本上成为大家的必备技能!网上也有很多中一键破解的软件,使root破解越来越容易.但是你思考过root破解的 原理吗?root破解的本质是什么呢?难道是利用了Lin ...
- [Android]Android系统启动流程源码分析
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...
- 【Android】【录音】Android录音--AudioRecord、MediaRecorder
[Android][录音]Android录音--AudioRecord.MediaRecorder Android提供了两个API用于实现录音功能:android.media.AudioRecord. ...
- Android智能手机中各种音频场景下的audio data path
上一篇文章(Android智能手机上的音频浅析)说本篇将详细讲解Android智能手机中各种音频场景下的音频数据流向,现在我们就开始.智能手机中音频的主要场景有音频播放.音频录制.语音通信等.不同场景 ...
- audacity 做音频分析之--初相识
软件介绍: Audacity是一个跨平台的声音编辑软件,用于录音和编辑音频,是自由.开放源代码的软件.可在Mac OS X.Microsoft Windows.GNU/Linux和其它操作系统上运作. ...
随机推荐
- primefaces 通过selectOneMenu更新显示隐藏区域
最重要的是update的区域要指定整个panel,而不是想更新的那个组件 <h:form id="frm"> <h:panelGrid id="pane ...
- Servlet3.0中Servlet的使用
目录 1.注解配置 2.异步调用 3.文件上传 相对于之前的版本,Servlet3.0中的Servlet有以下改进: l 支持注解配置. l 支持异步调用. l 直接有对文件上传的支持. 在这篇 ...
- SPOJ:ABCDEF
传送门 废话不说,这道题暴力枚举是$O(N^6)$,显然无法承受. 推导一下 $(x_1*x_2+x_3)/x_4-x_5=x_6$ $x_1*x_2+x_3=x_4*(x_5+x_6)$ 等式左边和 ...
- ELKstack搭建
开源实时日志分析ELK平台部署 官网地址:https://www.elastic.co/products 介绍: Elasticsearch是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现 ...
- js实现观察者模式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Blue tooth
一 . nordic BLE4.0 1.开发nordic的应用需要安装支持keil的pack库和插件 2.nordic的SDK很完整,实例涵盖了几乎所有的应用 https://www.nordicse ...
- [Unity] 查找资源
有时候需要通过代码来为对象指定一个资源.可以通过下面的函数来查找资源. /// <summary> /// 查找资源 /// </summary> /// <return ...
- jquery ajax 提交 FormData
$('form').submit(function(){ var formdata=new FormData(this); $.ajax({ type:'POST', url:'/url/path', ...
- ASP.NET MVC使用Bootstrap系列(1)——开始使用Bootstrap
阅读目录 Bootstrap结构介绍 在ASP.NET MVC 项目中添加Bootstrap文件 为网站创建Layout布局页 使用捆绑打包和压缩来提升网站性能 在Bootstrap项目中使用捆绑打包 ...
- QT实现贪吃蛇
board.h #ifndef BOARD_H #define BOARD_H #define MAX_X 40 #define MAX_Y 30 #define NORMAL_LABEL 0//普通 ...