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. Git分支学习简记

    简介 开始过了两遍Git的内容,第二天就已经忘记了分支(branch)的概念,开始还觉得不太用的到.然后又看了第二遍,才发现为什么大家说这个是Git里边极其重要的一个东西. 所谓branch,就类似于 ...

  2. 画虚线 iOS

    整理了一个方法,可以直接绘制虚线,下面直接上代码.参数说明已经给出,可直接copy使用 /** ** lineView: 需要绘制成虚线的view ** lineLength: 虚线的宽度 ** li ...

  3. 个人作业—Week3

    博客阅读体会 阅读了十几位软件工程师前辈的博文,了解了前辈们作为一名软件工程师的成长经历,我有一些感触. 这十几位前辈们的经历有着很大的差别,有的科班出身,有的则完全自学成才.不同的经历使得前辈们看问 ...

  4. HTML5 history

    引入history.pushState的来龙去脉 大家都知道web2.0以来,大家都喜欢使用ajax来请求数据,提高用户体验,但是传统的ajax可以无刷新改变页面内容,但无法改变页面URL,无刷新的改 ...

  5. CF721C. Journey

    传送门 说实话,这是一道非常简单的DP题,简单到如果放到NOIp第二题可能都有些差强人意,然而我写崩了. 所以简单记录一下. 需要注意的是,这道题的DP应该是从$N$点开始,以1为边界,满足最短路的三 ...

  6. elk系列3之通过json格式采集Nginx日志

    preface 公司采用的LNMP平台,跑着挺多nginx,所以可以利用elk好好分析nginx的日志.下面就聊聊它吧. 下面的所有操作都在linux-node2上操作 安装Nginx nginx是开 ...

  7. HD2767Proving Equivalences(有向图强连通分量+缩点)

    题目链接 题意:有n个节点的图,现在给出了m个边,问最小加多少边是的图是强连通的 分析:首先找到强连通分量,然后把每一个强连通分量缩成一个点,然后就得到了一个DAG.接下来,设有a个节点(每个节点对应 ...

  8. C#------对SQLServer进行简单的增,删,改,查

    EF中的操作转载: http://www.cnblogs.com/mcgrady/archive/2015/03/21/4355282.html PSContext db = new PSContex ...

  9. centos搭建 nginx一直报错 file not found.

    百度了半天找到别人的解决办法 记录下 摘要: file not found. nginx php 这个问题是你配置文件的问题: 查看就是了不要管 nginx 如何开启解析 PHP 的功能? # 成功安 ...

  10. yourphp超出20记录自动删除

    $m = M('service_loginlog'); $res =$m->where('card_id='.$_SESSION['card_id'])->order('time desc ...