How To Convert .pcm File To .wav Or .mp3?
Solution 1:
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:
privatevoidrawToWave(final File rawFile, final File waveFile)throws IOException {
byte[] rawData = newbyte[(int) rawFile.length()];
DataInputStreaminput=null;
try {
input = newDataInputStream(newFileInputStream(rawFile));
input.read(rawData);
} finally {
if (input != null) {
input.close();
}
}
DataOutputStreamoutput=null;
try {
output = newDataOutputStream(newFileOutputStream(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 = newshort[rawData.length / 2];
ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);
ByteBufferbytes= 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 {
intsize= (int) f.length();
byte bytes[] = newbyte[size];
byte tmpBuff[] = newbyte[size];
FileInputStream fis= newFileInputStream(f);
try {
intread= fis.read(bytes, 0, size);
if (read < size) {
intremain= 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;
}
privatevoidwriteInt(final DataOutputStream output, finalint value)throws IOException {
output.write(value >> 0);
output.write(value >> 8);
output.write(value >> 16);
output.write(value >> 24);
}
privatevoidwriteShort(final DataOutputStream output, finalshort value)throws IOException {
output.write(value >> 0);
output.write(value >> 8);
}
privatevoidwriteString(final DataOutputStream output, final String value)throws IOException {
for (inti=0; i < value.length(); i++) {
output.write(value.charAt(i));
}
}
How to use
It's quite simple to use. Just call it like this:
Filef1=newFile("/sdcard/44100Sampling-16bit-mono-mic.pcm"); // The location of your PCM fileFilef2=newFile("/sdcard/44100Sampling-16bit-mono-mic.wav"); // The location where you want your WAV filetry {
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. :)
Solution 2:
I know it is late and you got your stuff working with MediaRecorder. But thought of sharing my answer as it took me some good time to find it. :)
When you record your audio, the data is read
as short from your AudioRecord
object and it is then converted to bytes before storing in the .pcm
file.
Now, when you write the .wav
file, you're again doing the short conversion. This is not required. So, in your code if you remove the following block and write the rawData
directly to the end of .wav
file. It will work just fine.
short[] shorts = newshort[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);
}
Check the below piece of code you'll get after removing the duplicate block of code.
writeInt(output, rawData.length); // subchunk 2 size// removed the duplicate short conversion
output.write(rawData);
Solution 3:
Just to register, I solved my need of recording an audio playable in common players using MediaRecorder instead of Audio Recorder.
To start recording:
MediaRecordermRecorder=newMediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setAudioEncoder(MediaRecorder.OutputFormat.AMR_NB);
mRecorder.setOutputFile(Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/recording.3gp");
mRecorder.prepare();
mRecorder.start();
And to play the recording:
mPlayer = newMediaPlayer();
mPlayer.setDataSource(Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/recording.3gp");
mPlayer.prepare();
mPlayer.start();
Solution 4:
I tried the above code for audio recording writeAudioDataToFile()
. It perfectly records and convert the audio into .wav format
. But when I played the recorded audio, it was too fast. 5sec audio completed in 2.5 sec. Then I observed it was because of this short2byte()
function.
For those how are having same problem should not use short2byte()
and directly write sData in line os.write(sData, 0, Constants.BufferElements2Rec * Constants.BytesPerElement);
where sData should be byte[]
.
Post a Comment for "How To Convert .pcm File To .wav Or .mp3?"