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录音进行简单音频分析的更多相关文章

  1. Android.mk文件简单分析

    Android.mk文件简单分析 一个Android.mk文件用来向编译系统描写叙述须要编译的源码.详细来说:该文件是GNUMakefile的一小部分.会被编译系统解析一次或多次. 能够在每个Andr ...

  2. Android智能手机上的音频浅析

    手机可以说是现在人日常生活中最离不开的电子设备了.它自诞生以来,从模拟的发展到数字的,从1G发展到目前的4G以及不久将来的5G,从最初的只有唯一的功能(打电话)发展到目前的全功能,从功能机(featu ...

  3. Android 录音和播放

    今天工作上需要做一个一边录音一边播放的功能,大致原因是有一个外部设备输入音频到我们机器,然后我们机器需要马上把音频播放出来.所以了解了一些有关录音和播放的知识.接到这个任务的第一反应就是看看Andro ...

  4. Android智能手机上的音频浅析【转】

    本文转载自:https://blog.csdn.net/david_tym/article/details/80903385 手机可以说是现在人日常生活中最离不开的电子设备了.它自诞生以来,从模拟的发 ...

  5. (转)Android 系统 root 破解原理分析

    现在Android系统的root破解基本上成为大家的必备技能!网上也有很多中一键破解的软件,使root破解越来越容易.但是你思考过root破解的 原理吗?root破解的本质是什么呢?难道是利用了Lin ...

  6. [Android]Android系统启动流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...

  7. 【Android】【录音】Android录音--AudioRecord、MediaRecorder

    [Android][录音]Android录音--AudioRecord.MediaRecorder Android提供了两个API用于实现录音功能:android.media.AudioRecord. ...

  8. Android智能手机中各种音频场景下的audio data path

    上一篇文章(Android智能手机上的音频浅析)说本篇将详细讲解Android智能手机中各种音频场景下的音频数据流向,现在我们就开始.智能手机中音频的主要场景有音频播放.音频录制.语音通信等.不同场景 ...

  9. audacity 做音频分析之--初相识

    软件介绍: Audacity是一个跨平台的声音编辑软件,用于录音和编辑音频,是自由.开放源代码的软件.可在Mac OS X.Microsoft Windows.GNU/Linux和其它操作系统上运作. ...

随机推荐

  1. 2016.8.14安装myplayer心得

    安装mplayer时,我有两个os是not found状态,我在其他地方找到后 which mplayer,找到mplayer的配置界面,找到not found的部分,并且从usr/lib中找到相应的 ...

  2. JavaScript 写几个简单的知识点

    首先,还是用比较官方的文字描述来解释下JavaScript: JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为 ...

  3. 数据结构作业——order(二叉树遍历)

    order Description 给出一棵二叉树的中序遍历和每个节点的父节点,求这棵二叉树的先序和后 序遍历. Input 输入第一行为一个正整数 n 表示二叉树的节点数目, 节点编号从 1 到 n ...

  4. [Android]Volley源码分析(一)

    一. 如何使用Volley? 1. 首先定义一个RequestManager类,用来在Android程序启动时对Volley进行初始化.RequestManager为单例类,因为只有在程序启动时调用, ...

  5. JS, Node.js, npm简介

    序 听过JS,听过Node,也听过Node.js,还听过npm,然而并不是很清楚的知道都代表什么,这两天调接口,然后前端同学很忙,就自己把前端代码拿过来跑了,也趁机了解一下这几个概念,下边做个小的总结 ...

  6. JS性能方面--内存管理及ECMAScript5 Object的新属性方法

    Delete一个Object的属性会让此对象变慢(多耗费15倍的内存) var o = { x: 'y' }; delete o.x; //此时o会成一个慢对象 o.x; // var o = { x ...

  7. 修改/etc/profile导致常用命令不可用的解决办法

    原因:/etc/profile文件修改有误 解决办法: 用/usr/bin/vim /etc/profile进入,进去后修改正确/etc/profile,然后重启机器让该文件生效即可.

  8. AngularJs:String类型和JSON相互转换

    最近一周做了一个页面,制作的过程中遇到各种问题,从中可以看出本人的js基础还不够扎实,angularjs也只是刚入门的水平,现在将制作过程中遇到的问题一一汇总,方便以后查阅. 一.String类型和J ...

  9. 自然语言0_nltk中文使用和学习资料汇总

    http://blog.csdn.net/huyoo/article/details/12188573 官方数据 http://www.nltk.org/book/ Natural Language ...

  10. Set接口

    Set接口也是Collection接口的子接口,Set接口中不能加入重复的元素 Set接口的常用子类 1.散列的存放:HashSet HashSet是Set接口的一个子类,主要的特点是:里面不能存放重 ...