简体   繁体   中英

Converting a byte array (audio buffer from AudioRecord) to float array and vice versa

edit: I've edited the code to show my fruitless (and maybe completely stupid) attempt to solve the problem myself. With this code I only get an awful rattle-like sound.

I'm rather new to Android app development and now my uncle asked me to develop an app for him, which records audio and simultaneously plays it. As if this wasn't enough, he also wants me to add a frequency filter. Actually, that's beyond my skills, but I told him I would try, anyway.

I am able to record audio and play it with the RecordAudio and AudioTrack classes, respectively, but I have big problems with the frequency filter. I've used Google and searched this forum, of course, and could find some promising code snippets, but nothing really worked. This is the (working) code I have so far:

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

}

On the site, Filter AudioRecord Frequencies , I found a code, which uses FFT to filter frequencies:

I hope this code is correct, because - to be honest - I wouldn't know at all how to alter it, if it wasn't. But the actual problem is, that the audio buffer is a ByteArray , but I need a Float Array for the FFT with values between 0 and 1 (and after the reverse FFT I have to convert the float array back to a ByteArray ) .

I simply can't find code anywhere to do this, so any help would be highly appreciated!

byteToShort conversion is incorrect. While the data and most android devices are littlendian, ByteBuffer by default uses big-endian order. So we need to force it little-endian before conversion to short:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM