Java android实时获取声音频率?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20341229/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
android getting sound frequencies real time?
提问by Prerak Diwan
I have been trying to get the sound frequency(number) in real time using fft and i am having run time errors. can any one help?
我一直在尝试使用 fft 实时获取声音频率(数字),但出现运行时错误。有人可以帮忙吗?
package com.example.recordsound;
import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;
import ca.uol.aig.fftpack.RealDoubleFFT;
public class MainActivity extends Activity implements OnClickListener{
int audioSource = MediaRecorder.AudioSource.MIC; // Audio source is the device MIC
int channelConfig = AudioFormat.CHANNEL_IN_MONO; // Recording in mono
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; // Records in 16bit
private DoubleFFT_1D fft; // The fft double array
private RealDoubleFFT transformer;
int blockSize = 256; // deal with this many samples at a time
int sampleRate = 8000; // Sample rate in Hz
public double frequency = 0.0; // the frequency given
RecordAudio recordTask; // Creates a Record Audio command
TextView tv; // Creates a text view for the frequency
boolean started = false;
Button startStopButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView)findViewById(R.id.textView1);
startStopButton= (Button)findViewById(R.id.button1);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private class RecordAudio extends AsyncTask<Void, Double, Void>{
@Override
protected Void doInBackground(Void... params){
/*Calculates the fft and frequency of the input*/
//try{
int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioEncoding); // Gets the minimum buffer needed
AudioRecord audioRecord = new AudioRecord(audioSource, sampleRate, channelConfig, audioEncoding, bufferSize); // The RAW PCM sample recording
short[] buffer = new short[blockSize]; // Save the raw PCM samples as short bytes
// double[] audioDataDoubles = new double[(blockSize*2)]; // Same values as above, as doubles
// -----------------------------------------------
double[] re = new double[blockSize];
double[] im = new double[blockSize];
double[] magnitude = new double[blockSize];
// ----------------------------------------------------
double[] toTransform = new double[blockSize];
tv.setText("Hello");
// fft = new DoubleFFT_1D(blockSize);
try{
audioRecord.startRecording(); //Start
}catch(Throwable t){
Log.e("AudioRecord", "Recording Failed");
}
while(started){
/* Reads the data from the microphone. it takes in data
* to the size of the window "blockSize". The data is then
* given in to audioRecord. The int returned is the number
* of bytes that were read*/
int bufferReadResult = audioRecord.read(buffer, 0, blockSize);
// Read in the data from the mic to the array
for(int i = 0; i < blockSize && i < bufferReadResult; i++) {
/* dividing the short by 32768.0 gives us the
* result in a range -1.0 to 1.0.
* Data for the compextForward is given back
* as two numbers in sequence. Therefore audioDataDoubles
* needs to be twice as large*/
// audioDataDoubles[2*i] = (double) buffer[i]/32768.0; // signed 16 bit
//audioDataDoubles[(2*i)+1] = 0.0;
toTransform[i] = (double) buffer[i] / 32768.0; // signed 16 bit
}
//audiodataDoubles now holds data to work with
// fft.complexForward(audioDataDoubles);
transformer.ft(toTransform);
//------------------------------------------------------------------------------------------
// Calculate the Real and imaginary and Magnitude.
for(int i = 0; i < blockSize; i++){
// real is stored in first part of array
re[i] = toTransform[i*2];
// imaginary is stored in the sequential part
im[i] = toTransform[(i*2)+1];
// magnitude is calculated by the square root of (imaginary^2 + real^2)
magnitude[i] = Math.sqrt((re[i] * re[i]) + (im[i]*im[i]));
}
double peak = -1.0;
// Get the largest magnitude peak
for(int i = 0; i < blockSize; i++){
if(peak < magnitude[i])
peak = magnitude[i];
}
// calculated the frequency
frequency = (sampleRate * peak)/blockSize;
//----------------------------------------------------------------------------------------------
/* calls onProgressUpdate
* publishes the frequency
*/
publishProgress(frequency);
try{
audioRecord.stop();
}
catch(IllegalStateException e){
Log.e("Stop failed", e.toString());
}
}
// }
return null;
}
protected void onProgressUpdate(Double... frequencies){
//print the frequency
String info = Double.toString(frequencies[0]);
tv.setText(info);
}
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(started){
started = false;
startStopButton.setText("Start");
recordTask.cancel(true);
} else {
started = true;
startStopButton.setText("Stop");
recordTask = new RecordAudio();
recordTask.execute();
}
}
}
AS SOON AS I run the program with the OnClick it crashes I tried two libraries for fft but ran one at a time to see if the library works or not As soon as it reaches the line where I assign the the block size to the FFT object it crashes can any one help
只要我使用 OnClick 运行程序,它就会崩溃,我尝试了两个用于 fft 的库,但一次运行一个以查看该库是否有效一旦它到达我将块大小分配给 FFT 对象的那一行它崩溃了任何人都可以帮助
回答by Avanz
Try this FFT:
试试这个 FFT:
public class FFT {
int n, m;
// Lookup tables. Only need to recompute when size of FFT changes.
double[] cos;
double[] sin;
public FFT(int n) {
this.n = n;
this.m = (int) (Math.log(n) / Math.log(2));
// Make sure n is a power of 2
if (n != (1 << m))
throw new RuntimeException("FFT length must be power of 2");
// precompute tables
cos = new double[n / 2];
sin = new double[n / 2];
for (int i = 0; i < n / 2; i++) {
cos[i] = Math.cos(-2 * Math.PI * i / n);
sin[i] = Math.sin(-2 * Math.PI * i / n);
}
}
public void fft(double[] x, double[] y) {
int i, j, k, n1, n2, a;
double c, s, t1, t2;
// Bit-reverse
j = 0;
n2 = n / 2;
for (i = 1; i < n - 1; i++) {
n1 = n2;
while (j >= n1) {
j = j - n1;
n1 = n1 / 2;
}
j = j + n1;
if (i < j) {
t1 = x[i];
x[i] = x[j];
x[j] = t1;
t1 = y[i];
y[i] = y[j];
y[j] = t1;
}
}
// FFT
n1 = 0;
n2 = 1;
for (i = 0; i < m; i++) {
n1 = n2;
n2 = n2 + n2;
a = 0;
for (j = 0; j < n1; j++) {
c = cos[a];
s = sin[a];
a += 1 << (m - i - 1);
for (k = j; k < n; k = k + n2) {
t1 = c * x[k + n1] - s * y[k + n1];
t2 = s * x[k + n1] + c * y[k + n1];
x[k + n1] = x[k] - t1;
y[k + n1] = y[k] - t2;
x[k] = x[k] + t1;
y[k] = y[k] + t2;
}
}
}
}
}
It should address what you have in mind. If you decided to re-use it, give the proper credit to the author.
它应该解决您的想法。如果您决定重复使用它,请给予作者适当的荣誉。
Source/Author: EricLarch
来源/作者:EricLarch
回答by Sebastiano
If you really want to perform a real-timeaudio analysis, a Java-based approach won't do. I had a similar task in Q4 2013 for my company, and we decided to use Kiss FFT(perhaps the most simple FFT library with a BSD license), compiled for Android using the NDK.
如果您真的想执行实时音频分析,基于 Java 的方法是行不通的。我在 2013 年第 4 季度为我的公司完成了类似的任务,我们决定使用Kiss FFT(可能是最简单的具有 BSD 许可证的 FFT 库),使用 NDK 为 Android 编译。
A native C/C++ approach is tons of times fasterthan its Java counterpart. With the former, we have been able to perform real-time audio decodingand audio features analysison nearly every mid to high end device, something that was obviously impossible with the latter.
本机 C/C++ 方法比其 Java 对应方法快很多倍。使用前者,我们几乎可以在所有中高端设备上执行实时音频解码和音频特征分析,而后者显然是不可能做到的。
I stronglysuggest you to consider the native approach as your best option to do this task. Kiss FFT is a really simple library (literally stands for Keep It Simple FFT
), and you won't find much troubles in compiling and using it on Android. You won't be disappointed by the performance results.
我强烈建议您考虑将本机方法作为完成此任务的最佳选择。Kiss FFT 是一个非常简单的库(字面意思是Keep It Simple FFT
),在 Android 上编译和使用它不会有太多麻烦。您不会对性能结果感到失望。
回答by ???
Did you solved the problem? The crush is occurred because of the ArrayIndexOutOfBoundsException.
你解决了问题吗?由于 ArrayIndexOutOfBoundsException 发生粉碎。
So, modify your code to :
因此,将您的代码修改为:
double[] re = new double[blockSize];
double[] im = new double[blockSize];
double[] magnitude = new double[blockSize];
// Calculate the Real and imaginary and Magnitude.
for(int i = 0; i < blockSize+1; i++){
try {
// real is stored in first part of array
re[i] = toTransform[i * 2];
// imaginary is stored in the sequential part
im[i] = toTransform[(i * 2) + 1];
// magnitude is calculated by the square root of (imaginary^2 + real^2)
magnitude[i] = Math.sqrt((re[i] * re[i]) + (im[i] * im[i]));
}catch (ArrayIndexOutOfBoundsException e){
Log.e("test", "NULL");
}
}
double peak = -1.0;
// Get the largest magnitude peak
for(int i = 0; i < blockSize; i++){
if(peak < magnitude[i])
peak = magnitude[i];
}
// calculated the frequency
frequency = Double.toString((sampleRate * peak)/blockSize);