Writing Pcm Recorded Data into a .Wav File (Java Android)

Writing PCM recorded data into a .wav file (java android)

I've been wrestling with this exact same question for hours now, and my issue was mostly that when recording in 16 bits you have to be very careful about what you write to the output. The WAV file expects the data in Little Endian format, but using writeShort writes it to the output as Big Endian. I also got interesting results when using the other functions so I returned to writing bytes in the correct order and that works.

I used a Hex editor extensively while debugging this. I can recommend you do the same. Also, the header in the answer above works, I used it to check versus my own code and this header is rather foolproof.

Creating a WAV file from raw PCM data using the Android SDK

OK, I've got this figured out. This post was crucial in helping me:
http://computermusicblog.com/blog/2008/08/29/reading-and-writing-wav-files-in-java

Basically, I used ByteArrayOutputStream to write the raw PCM data from AudioRecord, which then lets me get the byte array and its size when the process is done. I can then use that data in conjunction with the SampleRate, BitRate, and Stereo/Mono settings to create the WAV header as per the link above. The resulting file works perfectly!

How to convert .pcm file to .wav or .mp3?

You've got most of the code correct. The only issue that I can see is the part where you write the PCM data to the WAV file. This should be quite simple to do because WAV = Metadata + PCM (in that order). This should work:

private void rawToWave(final File rawFile, final File waveFile) throws IOException {

byte[] rawData = new byte[(int) rawFile.length()];
DataInputStream input = null;
try {
input = new DataInputStream(new FileInputStream(rawFile));
input.read(rawData);
} finally {
if (input != null) {
input.close();
}
}

DataOutputStream output = null;
try {
output = new DataOutputStream(new FileOutputStream(waveFile));
// WAVE header
// see http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
writeString(output, "RIFF"); // chunk id
writeInt(output, 36 + rawData.length); // chunk size
writeString(output, "WAVE"); // format
writeString(output, "fmt "); // subchunk 1 id
writeInt(output, 16); // subchunk 1 size
writeShort(output, (short) 1); // audio format (1 = PCM)
writeShort(output, (short) 1); // number of channels
writeInt(output, 44100); // sample rate
writeInt(output, RECORDER_SAMPLERATE * 2); // byte rate
writeShort(output, (short) 2); // block align
writeShort(output, (short) 16); // bits per sample
writeString(output, "data"); // subchunk 2 id
writeInt(output, rawData.length); // subchunk 2 size
// Audio data (conversion big endian -> little endian)
short[] shorts = new short[rawData.length / 2];
ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);
ByteBuffer bytes = ByteBuffer.allocate(shorts.length * 2);
for (short s : shorts) {
bytes.putShort(s);
}

output.write(fullyReadFileToBytes(rawFile));
} finally {
if (output != null) {
output.close();
}
}
}
byte[] fullyReadFileToBytes(File f) throws IOException {
int size = (int) f.length();
byte bytes[] = new byte[size];
byte tmpBuff[] = new byte[size];
FileInputStream fis= new FileInputStream(f);
try {

int read = fis.read(bytes, 0, size);
if (read < size) {
int remain = size - read;
while (remain > 0) {
read = fis.read(tmpBuff, 0, remain);
System.arraycopy(tmpBuff, 0, bytes, size - remain, read);
remain -= read;
}
}
} catch (IOException e){
throw e;
} finally {
fis.close();
}

return bytes;
}
private void writeInt(final DataOutputStream output, final int value) throws IOException {
output.write(value >> 0);
output.write(value >> 8);
output.write(value >> 16);
output.write(value >> 24);
}

private void writeShort(final DataOutputStream output, final short value) throws IOException {
output.write(value >> 0);
output.write(value >> 8);
}

private void writeString(final DataOutputStream output, final String value) throws IOException {
for (int i = 0; i < value.length(); i++) {
output.write(value.charAt(i));
}
}

How to use

It's quite simple to use. Just call it like this:

  File f1 = new File("/sdcard/44100Sampling-16bit-mono-mic.pcm"); // The location of your PCM file
File f2 = new File("/sdcard/44100Sampling-16bit-mono-mic.wav"); // The location where you want your WAV file
try {
rawToWave(f1, f2);
} catch (IOException e) {
e.printStackTrace();
}

How all this works

As you can see, the WAV header is the only difference between WAV and PCM file formats. The assumption is that you are recording 16 bit PCM MONO audio (which according to your code, you are). The rawToWave function just neatly adds headers to the WAV file, so that music players know what to expect when your file is opened, and then after the headers, it just writes the PCM data from the last bit onwards.

Cool Tip

If you want to shift the pitch of your voice, or make a voice changer app, all you got to do is increase/decrease the value of writeInt(output, 44100); // sample rate in your code. Decreasing it will tell the player to play it at a different rate thereby changing the output pitch. Just a little extra 'good to know' thing. :)

AudioRecord writing/reading raw PCM data to file

It was all in the header

http://gitorious.org/android-eeepc/base/blobs/48276ab989a4d775961ce30a43635a317052672a/core/java/android/speech/srec/WaveHeader.java

Once I fixed that everything was fine.



Related Topics



Leave a reply



Submit