Java Android应用程序可实时录制声音并识别频率
我需要开发一个应用程序,使用手机的麦克风实时记录频率,然后显示它们(文本)。我在这里张贴我的代码。FFT和复数类是从和开始使用的。问题是,当我在模拟器上运行时,频率从某个随机值开始,一直增加到7996。然后重复整个过程。有人能帮我吗Java Android应用程序可实时录制声音并识别频率,java,android,fft,android-audiorecord,frequency-analysis,Java,Android,Fft,Android Audiorecord,Frequency Analysis,我需要开发一个应用程序,使用手机的麦克风实时记录频率,然后显示它们(文本)。我在这里张贴我的代码。FFT和复数类是从和开始使用的。问题是,当我在模拟器上运行时,频率从某个随机值开始,一直增加到7996。然后重复整个过程。有人能帮我吗 public class Main extends Activity { TextView disp; private static int[] sampleRate = new int[] { 44100, 22050, 11025, 8000 }; short
public class Main extends Activity {
TextView disp;
private static int[] sampleRate = new int[] { 44100, 22050, 11025, 8000 };
short audioData[];
double finalData[];
int bufferSize,srate;
String TAG;
public boolean recording;
AudioRecord recorder;
Complex[] fftArray;
float freq;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
disp = (TextView) findViewById(R.id.display);
Thread t1 = new Thread(new Runnable(){
public void run() {
Log.i(TAG,"Setting up recording");
for (int rate : sampleRate) {
try{
Log.d(TAG, "Attempting rate " + rate);
bufferSize=AudioRecord.getMinBufferSize(rate,AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT)*3; //get the buffer size to use with this audio record
if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {
recorder = new AudioRecord (MediaRecorder.AudioSource.MIC,rate,AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,2048); //instantiate the AudioRecorder
Log.d(TAG, "BufferSize " +bufferSize);
srate = rate;
}
} catch (Exception e) {
Log.e(TAG, rate + "Exception, keep trying.",e);
}
}
bufferSize=2048;
recording=true; //variable to use start or stop recording
audioData = new short [bufferSize]; //short array that pcm data is put into.
Log.i(TAG,"Got buffer size =" + bufferSize);
while (recording) { //loop while recording is needed
Log.i(TAG,"in while 1");
if (recorder.getState()==android.media.AudioRecord.STATE_INITIALIZED) // check to see if the recorder has initialized yet.
if (recorder.getRecordingState()==android.media.AudioRecord.RECORDSTATE_STOPPED)
recorder.startRecording(); //check to see if the Recorder has stopped or is not recording, and make it record.
else {
Log.i(TAG,"in else");
// audiorecord();
finalData=convert_to_double(audioData);
Findfft();
for(int k=0;k<fftArray.length;k++)
{
freq = ((float)srate/(float) fftArray.length) *(float)k;
runOnUiThread(new Runnable(){
public void run()
{
disp.setText("The frequency is " + freq);
if(freq>=15000)
recording = false;
}
});
}
}//else recorder started
} //while recording
if (recorder.getState()==android.media.AudioRecord.RECORDSTATE_RECORDING)
recorder.stop(); //stop the recorder before ending the thread
recorder.release(); //release the recorders resources
recorder=null; //set the recorder to be garbage collected.
}//run
});
t1.start();
}
private void Findfft() {
// TODO Auto-generated method stub
Complex[] fftTempArray = new Complex[bufferSize];
for (int i=0; i<bufferSize; i++)
{
fftTempArray[i] = new Complex(finalData[i], 0);
}
fftArray = FFT.fft(fftTempArray);
}
private double[] convert_to_double(short data[]) {
// TODO Auto-generated method stub
double[] transformed = new double[data.length];
for (int j=0;j<data.length;j++) {
transformed[j] = (double)data[j];
}
return transformed;
}
@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;
}
}
public类主扩展活动{
文本视图显示;
私有静态int[]sampleRate=newint[]{4410022050110258000};
短音频数据[];
双重财务数据[];
int bufferSize,srate;
字符串标签;
公共录音;
录音机;
复合放射性;
浮动频率;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
disp=(TextView)findViewById(R.id.display);
线程t1=新线程(新的可运行线程(){
公开募捐{
Log.i(标签“设置记录”);
用于(整数率:采样率){
试一试{
Log.d(标签“尝试速率”+速率);
bufferSize=AudioRecord.getMinBufferSize(速率、AudioFormat.CHANNEL\u配置\u单声道、,
AudioFormat.ENCODING_PCM_16BIT)*3;//获取用于此音频记录的缓冲区大小
if(bufferSize!=录音。错误\u错误\u值){
recorder=新的录音(MediaRecorder.AudioSource.MIC、速率、AudioFormat.CHANNEL\u配置\u MONO、,
AudioFormat.ENCODING_PCM_16BIT,2048);//实例化录音机
Log.d(标记“BufferSize”+BufferSize);
srate=速率;
}
}捕获(例外e){
Log.e(标记,速率+“异常,继续尝试”,e);
}
}
缓冲区大小=2048;
recording=true;//用于开始或停止录制的变量
audioData=new short[bufferSize];//将pcm数据放入的短数组。
Log.i(标记“Got buffer size=“+bufferSize”);
while(录制){//需要录制时循环
Log.i(标记为“in while 1”);
if(recorder.getState()==android.media.AudioRecord.STATE\u INITIALIZED)//检查记录器是否已初始化。
if(recorder.getRecordingState()==android.media.AudioRecord.RECORDSTATE\u已停止)
recorder.startRecording();//检查记录器是否已停止或未录制,并将其录制下来。
否则{
Log.i(标记“in-else”);
//录音带();
finalData=将音频数据转换为双音频数据;
findft();
对于(整数k=0;k=15000)
记录=假;
}
});
}
}//其他记录器已启动
}//录制时
if(recorder.getState()==android.media.AudioRecord.RECORDSTATE\u录制)
recorder.stop();//在结束线程之前停止记录器
recorder.release();//释放记录器资源
recorder=null;//将记录器设置为垃圾收集。
}//跑
});
t1.start();
}
私有void findft(){
//TODO自动生成的方法存根
Complex[]fftTempArray=新的Complex[bufferSize];
对于(int i=0;i您的问题就在这里:
Findfft();
for(int k=0;k<fftArray.length;k++) {
freq = ((float)srate/(float) fftArray.length) *(float)k;
runOnUiThread(new Runnable() {
public void run() {
disp.setText("The frequency is " + freq);
if(freq>=15000) recording = false;
}
});
}
findft();
对于(int k=0;k=15000)记录=假;
}
});
}
for循环所做的一切就是遍历FFT值数组,将数组索引转换为以Hz为单位的频率,然后打印出来
如果你想输出你正在记录的频率,你至少应该看看你数组中的数据——最粗糙的方法是计算平方实震级,找到最大的频率
除此之外,我认为您正在使用的FFT算法不会进行任何预计算-还有其他算法会进行预计算,并且在为移动设备开发时,您可能需要考虑CPU使用和电源使用
是一个使用预计算来降低CPU负载的库,它的文档非常完整
您还可以找到关于如何解释从FFT返回的数据的有用信息-无意冒犯,但看起来您不太确定自己在做什么,所以我给出了一些提示
最后,如果你想用这个应用程序来做音符,我似乎记得很多人都说FFT不是最好的方法,但我不记得是什么。也许其他人可以添加这一点?您的问题已经得到了简洁的回答,但是,为了进一步实现您的目标并完成循环
是的,对于基音/频率识别,FFT在有限的CPU上不是最佳的。本文描述了一种更为优化的方法。您可以在上找到一个实现。
您将面临的问题是ADK中缺少javax.sound.sampled,因此无法将音频记录中的短/字节转换为参考实现所需的浮点值。几天后,我找到了此解决方案-在Hrz中获得频率的最佳方法:
下载并下载-JTTransforms需要它
然后我使用这个任务:
public class MyRecorder extends AsyncTask<Void, short[], Void> {
int blockSize = 2048;// = 256;
private static final int RECORDER_SAMPLERATE = 8000;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
int BufferElements2Rec = 1024; // want to play 2048 (2K) since 2 bytes we use only 1024
int BytesPerElement = 2;
@Override
protected Void doInBackground(Void... params) {
try {
final AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
RECORDER_SAMPLERATE, RECORDER_CHANNELS,
RECORDER_AUDIO_ENCODING, BufferElements2Rec * BytesPerElement);
if (audioRecord == null) {
return null;
}
final short[] buffer = new short[blockSize];
final double[] toTransform = new double[blockSize];
audioRecord.startRecording();
while (started) {
Thread.sleep(100);
final int bufferReadResult = audioRecord.read(buffer, 0, blockSize);
publishProgress(buffer);
}
audioRecord.stop();
audioRecord.release();
} catch (Throwable t) {
Log.e("AudioRecord", "Recording Failed");
}
return null;
}
@Override
protected void onProgressUpdate(short[]... buffer) {
super.onProgressUpdate(buffer);
float freq = calculate(RECORDER_SAMPLERATE, buffer[0]);
}
public static float calculate(int sampleRate, short [] audioData)
{
int numSamples = audioData.length;
int numCrossing = 0;
for (int p = 0; p < numSamples-1; p++)
{
if ((audioData[p] > 0 && audioData[p + 1] <= 0) ||
(audioData[p] < 0 && audioData[p + 1] >= 0))
{
numCrossing++;
}
}
float numSecondsRecorded = (float)numSamples/(float)sampleRate;
float numCycles = numCrossing/2;
float frequency = numCycles/numSecondsRecorded;
return frequency;
}
公共类MyRecorder扩展异步任务{
int blockSize=2048;//=256;
专用静态最终积分记录仪\u采样器=8000;
专用静态最终整数记录器\u通道=单声道中的AudioFormat.CHANNEL\u;
专用静态最终整数记录器\u音频\u编码=AudioFormat.ENCODING\u PCM\u 16位;
int BufferElements2Rec=1024;//要播放2048(2K),因为我们只使用1024个字节
int bytesperement=2;
@凌驾
受保护的Void doInBackground(Void…参数){
试一试{
最终音频记录=新音频记录(MediaRecorder.AudioSource.MIC,
记录仪采样器、记录仪通道、,
记录器\音频\编码,缓冲元素2REC*BytesPerElement);
如果(音频记录==null