通过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和其它操作系统上运作. ...
随机推荐
- AngularJs ngList、ngRepeat、ngModelOptions
ngList 在文本输入的分隔的字符串和字符串数组间做转换,可以是一个固定的字符串分隔符(默认逗号)或正则表达式. 格式:ng-list=”value” value:表达式 通过这个值分隔字符串. ...
- HDU 3530 Subsequence(单调队列)
传送门 Description There is a sequence of integers. Your task is to find the longest subsequence that s ...
- Pythonn new-style class and old-style class
In [1]: class old(): ...: a = 1 ...: In [2]: o = old() In [3]: o.__class__ Out[3]: <class __main_ ...
- CTO、技术总监、首席架构师的区别
2016年11月30日13:22:26[转] CTO.技术总监.首席架构师的区别 提升自已的能力,比如专业技术,行业发展趋势,技术发展趋势,协调能力,组织能力,管理能力等[技术总监] 需要从技术总监和 ...
- 会话控制:session与cookie
我们在浏览网站时,访问的每一个web页面都需要使用"http协议"实现.而HTTP协议是无状态协议,就是说HTTP协议没有一个内建机制来维护两个事务之间的状态.当一个用户请求一个页 ...
- 自然语言7_NLTK中文语料库sinica_treebank
http://www.hankcs.com/program/python/nltk-chinese-corpus-sinica_treebank.html NLTK包含Sinica (中央研究院)提供 ...
- 9月9日下午HTML样式表(宽度和高度、背景字体、对齐方式边界与边框)
样式表 一.大小 1.width 宽度 2.height 高度 <div style="width:200px; height:200px"></div&g ...
- Eratosthenes筛选法构造1-n 素数表
筛选法:对于不超过n的每个非负整数p,删除2p,3p,4p...当处理完所有数之后,还没没删除的就是素数. 代码中进行了相应的优化. 本代码功能,输入一个数,输出从1-该数之间的素数.功能待完善,可将 ...
- thinkphp删除
$result = M('content')->where('id>0')->delete $result =M('content')->where(array('id'=&g ...
- List.Sort用法
treeList.Sort((x, y) => x.SortIndex < y.SortIndex ? -1 : 0);