Question

For the past 2 days I've been trying to manipulate 16 bit PCM data on Android with little success. I'm currently using WAV recorder to capture audio. In the onPeriodicNotification(AudioRecord recorder) method before the buffer is written with the randomAccessWriter I send the buffer to a custom class, to manipulate the samples, and save the samples back into the buffer. The method in my custom class is as follows:

As the buffer is a byte array I first convert them into shorts, now one short represents a frame (there's only one channel). I will be implementing FFT algorithms, once I get past this hurdle, that need the input to be a float array - so I convert each short into a float. Now, the randomAccessWriter that writes the data into the WAV file accepts a byte array and is expecting each frame to be 2 bytes. Therefore I convert each float back into a short and use a ByteBuffer to reconstruct a byte array, which is then returned. When I run my recorder app, with the buffer being sent through the above code, everything is fine.

I try using a simple voice modulation algorithm to test if the recording is modified, the algorithm is placed where the TODO comment is:

Now if I used the above code on my iPhone the audio samples would be transformed, although the data is natively 32bit floats. However, on Android when I re-run the recorder app, with the above code inserted, all that's produced is white noise. Until I can successfully modify the samples with the above code, I can't proceed with my FFT algorithms.

Why is this occurring? I would be grateful if someone with knowledge on the topic could shed light on the topic.

SOLVED - By Bjorn Roche

Underlying cause: Recording was giving data in Little Endian whereas Java shorts are in Big Endian; when applying a function using the two different forms, white noise is produced. The below code shows how to take in a Little Endian byte array, convert to Big Endian float array and back to Little Endian byte array. Whilst floats you can do whatever you please, I'll now be using my FFT algorithms:

public byte[] manipulateSamples(byte[] data,
                                int samplingRate,
                                int numFrames,
                                short numChannels) {

    // Convert byte[] to short[] (16 bit) to float[] (32 bit) (End result: Big Endian)
    ShortBuffer sbuf = ByteBuffer.wrap(data).asShortBuffer();
    short[] audioShorts = new short[sbuf.capacity()];
    sbuf.get(audioShorts);

    float[] audioFloats = new float[audioShorts.length];

    for (int i = 0; i < audioShorts.length; i++) {
        audioFloats[i] = ((float)Short.reverseBytes(audioShorts[i])/0x8000);
    }

    // Do your tasks here.

    // Convert float[] to short[] to byte[] (End result: Little Endian)
    audioShorts = new short[audioFloats.length];
    for (int i = 0; i < audioFloats.length; i++) {
        audioShorts[i] = Short.reverseBytes((short) ((audioFloats[i])*0x8000));
    }

    byte byteArray[] = new byte[audioShorts.length * 2];
    ByteBuffer buffer = ByteBuffer.wrap(byteArray);
    sbuf = buffer.asShortBuffer();
    sbuf.put(audioShorts);
    data = buffer.array();

    return data;

}
Was it helpful?

Solution

Your problem is that shorts in java are bigendian, but if you got your data from a WAV file the data is little endian.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top