我有一个声音文件(.3gp),大概约1分钟。我想每1/4秒获得一次这个声音文件的频率。我的想法是每隔1/4秒从音频文件接收采样并使用FFT我可能得到频率值。有没有办法做到这一点?
实际上我会将声音文件分成1 / 4sec样本声音文件(alwyas覆盖这些声音文件),然后使用FFT算法并检测magintude最大的频率。但是可能有更简单的解决方案,但我也不知道如何做到这一点。
***更新2 - 新代码
到目前为止我使用此代码:
public class RecordAudio extends AsyncTask<Void, double[], Void> {
@Override
protected Void doInBackground(Void... arg0) {
try {
int bufferSize = AudioRecord.getMinBufferSize(frequency,
AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
//int bufferSize = AudioRecord.getMinBufferSize(frequency,
// channelConfiguration, audioEncoding);
AudioRecord audioRecord = new AudioRecord(
MediaRecorder.AudioSource.MIC, frequency,
channelConfiguration, audioEncoding, bufferSize);
short[] buffer = new short[blockSize];
//double[] toTransform = new double[blockSize];
audioRecord.startRecording();
// started = true; hopes this should true before calling
// following while loop
while (started) {
sampling++;
double[] re = new double[blockSize];
double[] im = new double[blockSize];
double[] newArray = new double[blockSize*2];
double[] magns = new double[blockSize];
double MaxMagn=0;
double pitch = 0;
int bufferReadResult = audioRecord.read(buffer, 0,
blockSize);
for (int i = 0; i < blockSize && i < bufferReadResult; i++) {
re[i] = (double) buffer[i] / 32768.0; // signed 16bit
im[i] = 0;
}
newArray = FFTbase.fft(re, im,true);
for (int i = 0; i < newArray.length; i+=2) {
re[i/2]=newArray[i];
im[i/2]=newArray[i+1];
magns[i/2] = Math.sqrt(re[i/2]*re[i/2]+im[i/2]*im[i/2]);
}
// I only need the first half
for (int i = 0; i < (magns.length)/2; i++) {
if (magns[i]>MaxMagn)
{
MaxMagn = magns[i];
pitch=i;
}
}
if (sampling > 50) {
Log.i("pitch and magnitude", "" + MaxMagn + " " + pitch*15.625f);
sampling=0;
MaxMagn=0;pitch=0;
}
}
audioRecord.stop();
} catch (Throwable t) {
t.printStackTrace();
Log.e("AudioRecord", "Recording Failed");
}
return null;
}
我用这个:http://www.wikijava.org/wiki/The_Fast_Fourier_Transform_in_Java_%28part_1%29
吉他弦似乎是正确的,但我自己的声音并不好,因为:
两个峰值的大小在大部分时间都会发生变化,我总能找到最大值来获得基频。
答案 0 :(得分:7)
在Stack Overflow上经常询问使用FFT的音高跟踪我写了blog entry with sample code。代码在C中,但通过解释和链接,您应该能够做到你想要的。
至于将其分成1/4秒的增量,您可以按照建议的方式简单地采用1/4秒的FFT,而不是默认值(我认为大约是1秒)。如果这不能提供您想要的频率分辨率,则可能必须使用不同的音高识别方法。您可以做的另一件事是使用长度超过1/4秒的重叠段,但是以相隔1/4秒的间隔开始。此方法是博客条目的提法,但可能不符合您的设计规范。
答案 1 :(得分:1)
尝试 AsyncTask :
class GetFrequency extends AsyncTask<String, Void, Void> {
public Void doInBackground(String... params) {
while (true) {
// Apply Logic Here
try {
Thread.sleep(250);
} catch (Exception ie) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
通过
在MainActivity中调用此方法frequencyButtonListener.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
new GetFrequency.execute(params);
}
});