将字节数组(音频缓冲区从AudioRecord)转换为浮点数组,反之亦然

时间:2014-08-07 12:50:14

标签: android arrays audio

编辑:我编辑了代码,以显示我自己没有成功(也许是完全愚蠢)的尝试来解决问题。使用这段代码,我只会听到一声巨响。

我对Android应用程序开发很陌生,现在我的叔叔让我为他开发一个应用程序,它可以录制音频并同时播放它。好像这还不够,他还要我添加一个频率滤波器。实际上,这超出了我的技能,但无论如何,我告诉他我会尝试。

我能够分别录制音频和播放RecordAudioAudioTrack类,但我的频率滤波器存在很大问题。当然,我已经使用谷歌并搜索了这个论坛,并且可以找到一些有希望的代码片段,但没有真正有效。 这是我到目前为止的(工作)代码:

public class MainActivity extends ActionBarActivity {
float freq_min;
float freq_max;     
boolean isRecording = false;
int SAMPLERATE = 8000;
int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
Thread recordingThread = null;  
AudioRecord recorder;
Button cmdPlay;
EditText txtMinFrequency, txtMaxFrequency;  

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    cmdPlay = (Button)findViewById(R.id.bPlay);
    cmdPlay.setOnClickListener(onClickListener);
    txtMinFrequency = (EditText)findViewById(R.id.frequency_min);
    txtMaxFrequency = (EditText)findViewById(R.id.frequency_max);        
}

private OnClickListener onClickListener = new OnClickListener() {       
    @Override
    public void onClick(View v) {           
        if (!isRecording) {
            freq_min = Float.parseFloat(txtMinFrequency.getText().toString());
            freq_max = Float.parseFloat(txtMaxFrequency.getText().toString());
            isRecording = true;
            cmdPlay.setText("stop");
            startRecording();               
        }
        else {
            isRecording = false;
            cmdPlay.setText("play");
            stopRecording();                
        }           
    }
};

public void startRecording() {
    recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLERATE, 
            AudioFormat.CHANNEL_IN_MONO, AUDIO_FORMAT, 1024);
    recorder.startRecording();

    recordingThread = new Thread(new Runnable(){
        public void run() {
            recordAndWriteAudioData();
        }
    });
    recordingThread.start();

}

public void stopRecording() {
    isRecording = false;
    recorder.stop();
    recorder.release();
    recorder = null;
    recordingThread = null;
}

private void recordAndWriteAudioData() { 
    byte audioData[] = new byte[1024];
    AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLERATE, AudioFormat.CHANNEL_OUT_MONO,
            AudioFormat.ENCODING_PCM_16BIT, 1024, AudioTrack.MODE_STREAM);
    at.play();

    while (isRecording) {
        recorder.read(audioData, 0, 1024);

        // Converting from byte array to float array and dividing floats by 32768 to get values between 0 and 1
        float[] audioDataF = shortToFloat(byteToShort(audioData));  
        for (int i = 0; i < audioDataF.length; i++) {
            audioDataF[i] /= 32768.0; 
        }

        // Fast Fourier Transform
        FloatFFT_1D fft = new FloatFFT_1D(512);
        fft.realForward(audioDataF);

        // fiter frequencies
        for(int fftBin = 0; fftBin < 512; fftBin++){                
            float frequency = (float)fftBin * (float)SAMPLERATE / (float)512;               
            if(frequency  < freq_min || frequency > freq_max){                 
                int real = 2 * fftBin;
                int imaginary = 2 * fftBin + 1;                 
                audioDataF[real] = 0;
                audioDataF[imaginary] = 0;
            }
        }

        //inverse FFT
        fft.realInverse(audioDataF, false); 

        // multiplying the floats by 32768
        for (int i = 0; i < audioDataF.length; i++) {
            audioDataF[i] *= 32768.0;
        }

        // converting float array back to short array
        audioData = shortToByte(floatToShort(audioDataF));

        at.write(audioData, 0, 1024);

    }
    at.stop();
    at.release();
}

public static short[] byteToShort (byte[] byteArray){
    short[] shortOut = new short[byteArray.length / 2];
    ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
    for (int i = 0; i < shortOut.length; i++) {
        shortOut[i] = byteBuffer.getShort();
    }       
    return shortOut;
}

public static float[] shortToFloat (short[] shortArray){
    float[] floatOut = new float[shortArray.length];
    for (int i = 0; i < shortArray.length; i++) {
        floatOut[i] = shortArray[i]; 
    }
    return floatOut;
}

public static short[] floatToShort (float[] floatArray){
    short[] shortOut = new short[floatArray.length];
    for (int i = 0; i < floatArray.length; i++) {
        shortOut[i] = (short) floatArray[i]; 
    }
    return shortOut;
}

public static byte[] shortToByte (short[] shortArray){
    byte[] byteOut = new byte[shortArray.length * 2];
    ByteBuffer.wrap(byteOut).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(shortArray);
    return byteOut;
}

@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;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}   

}

在网站Filter AudioRecord Frequencies上,我找到了一个使用 FFT 过滤频率的代码:

我希望这段代码是正确的,因为 - 说实话 - 我根本不知道如何改变它,如果不是的话。但实际问题是,音频缓冲区是ByteArray,但我需要Float Array用于FFT,其值介于 0 1 之间(在反向FFT之后,我必须将float array转换回ByteArray

我无法在任何地方找到代码来执行此操作,因此任何帮助都将受到高度赞赏!

1 个答案:

答案 0 :(得分:1)

byteToShort 转换不正确。虽然数据和大多数Android设备都是littlendian,但 ByteBuffer 默认使用big-endian顺序。所以我们需要在转换为short之前强制它为little-endian:

public static short[] byteToShort (byte[] byteArray){
    short[] shortOut = new short[byteArray.length / 2];
    ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
    byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shortOut);
    return shortOut;
}