0

I record some audio using libgdx AudioRecorder which return short[]. I think I´ve converted it into byte[] instead so it can be stored in a file cause I need the audio to be able to playback anytime. Here´s some variables I use:

final int samples = 44100;
boolean isMono = true;
final short[] data = new short[samples * 5];
final AudioRecorder recorder = Gdx.audio.newAudioRecorder(samples, isMono);
final AudioDevice player = Gdx.audio.newAudioDevice(samples, isMono);

And here I start and play the audio, also I think I´ve converted the short[] into byte[] , correct me if Im wrong.

 public void startRecord(){
    new Thread(new Runnable() {
        @Override
        public void run() {
                System.out.println("Record start:");
                recorder.read(data, 0, data.length);
                recorder.dispose();
                ShortBuffer buffer = BufferUtils.newShortBuffer(2);
                buffer.put(data[1]);
        }
    }).start();

}

public void playRecorded(){
    new Thread(new Runnable() {
        @Override
        public void run() {
            player.writeSamples(data, 0, data.length);
            player.dispose();
        }
    }).start();
}

Here´s an example on how I´ve stored byte[] before. But I can´t implement this teqhnique on this method. Bare in mind I need it to be a libgdx solution.

public void onImagePicked(final InputStream stream) {
                loading = "Loading";
                pending = executor.submit(new AsyncTask<Pixmap>() {
                    @Override
                    public Pixmap call() throws Exception {
                        StreamUtils.copyStream(stream, file.write(false));
                         final byte[] bytes = file.readBytes();
                        final Pixmap pix = new Pixmap(bytes, 0, bytes.length);
                        return pix;

                    }
                });
            }
Benni
  • 969
  • 1
  • 19
  • 29
  • Are you sure about `buffer.put(data[1]);` ? You are putting only one array element in the buffer. And you don't seem to use it. – Tanmay Patil May 02 '15 at 14:23
  • Im not sure about anything else than that this record function works. Im all ears on how to do the rest! – Benni May 02 '15 at 14:26
  • See http://stackoverflow.com/questions/10804852/how-to-convert-short-array-to-byte-array and http://stackoverflow.com/questions/5625573/byte-array-to-short-array-and-back-again-in-java – P.T. May 02 '15 at 19:33

1 Answers1

1

Hi it is actually very simple. You can convert a byte array to short like this:

// get all bytes
byte[] temp = ... 
// create short with half the length (short = 2 bytes)
short[] data = new short[temp.length / 2]; 

// cast a byte array to short array
ByteBuffer.wrap(temp).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(data); 

//data now has the short array in it

You can convert a short array to byte like this:

// audio data
short[] data = ...; 
//create a byte array to hold the data passed (short = 2 bytes)
byte[] temp = new byte[data.length * 2]; 

// cast a short array to byte array
ByteBuffer.wrap(temp).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(data); 

// temp now has the byte array

Sample project

I created a sample project in github that you can clone to see how this works.

Sample project here

However this is the Game class for the sample project if you only want to take a quick look at it:

package com.leonziyo.recording;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.AudioDevice;
import com.badlogic.gdx.audio.AudioRecorder;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.utils.GdxRuntimeException;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class MainGame extends ApplicationAdapter {

    boolean recordTurn = true;

    final int samples = 44100;
    boolean isMono = true, recording = false, playing = false;

    @Override
    public void create () {}

    @Override
    public void render () {
        /*Changing the color just to know when it is done recording or playing audio (optional)*/
        if(recording)
            Gdx.gl.glClearColor(1, 0, 0, 1);
        else if(playing)
            Gdx.gl.glClearColor(0, 1, 0, 1);
        else
            Gdx.gl.glClearColor(0, 0, 1, 1);

        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        // We trigger recording and playing with touch for simplicity
        if(Gdx.input.justTouched()) {
            if(recordTurn)
                recordToFile("sound.bin", 3); //pass file name and number of seconds to record
            else
                playFile("sound.bin"); //file name to play

            recordTurn = !recordTurn;
        }
    }

    @Override
    public void dispose() {

    }

    private void recordToFile(final String filename, final int seconds) {
        //Start a new thread to do the recording, because it will block and render won't be called if done in the main thread
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    recording = true;

                    short[] data = new short[samples * seconds];
                    AudioRecorder recorder = Gdx.audio.newAudioRecorder(samples, isMono);
                    recorder.read(data, 0, data.length);
                    recorder.dispose();
                    saveAudioToFile(data, filename);
                }
                catch(GdxRuntimeException e) {
                    Gdx.app.log("test", e.getMessage());
                }
                finally {
                    recording = false;
                }
            }
        }).start();

    }

    private void playFile(final String filename) {
        //Start a new thread to play the file, because it will block and render won't be called if done in the main thread
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    playing = true;
                    short[] data = getAudioDataFromFile(filename); //get audio data from file

                    AudioDevice device = Gdx.audio.newAudioDevice(samples, isMono);
                    device.writeSamples(data, 0, data.length);

                    device.dispose();
                }
                catch(GdxRuntimeException e) {
                    Gdx.app.log("test", e.getMessage());
                }
                finally {
                    playing = false;
                }
            }
        }).start();
    }

    private short[] getAudioDataFromFile(String filename) {
        FileHandle file = Gdx.files.local(filename);
        byte[] temp = file.readBytes(); // get all bytes from file
        short[] data = new short[temp.length / 2]; // create short with half the length (short = 2 bytes)

        ByteBuffer.wrap(temp).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(data); // cast a byte array to short array

        return data;
    }

    private void saveAudioToFile(short[] data, String filename) {
        byte[] temp = new byte[data.length * 2]; //create a byte array to hold the data passed (short = 2 bytes)

        ByteBuffer.wrap(temp).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(data); // cast a short array to byte array

        FileHandle file = Gdx.files.local(filename);
        file.writeBytes(temp, false); //save bytes to file
    }

}

Don't forget to add permission in case you are writing to external storage and permission for audio recording:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Leo
  • 834
  • 8
  • 14