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

I am currently developing an Android application that has audio recording and playback. I am new to audio and am having problems with encoding and formats.

I can record and play audio in my application, but when exporting, I cannot play sound. The only way I found is to export my .pcm file and convert it using Audacity.

This is my code for recording sound:

private Thread recordingThread private AudioRecord mRecorder; private boolean isRecording = false; private void startRecording() { mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, Constants.RECORDER_SAMPLERATE, Constants.RECORDER_CHANNELS, Constants.RECORDER_AUDIO_ENCODING, Constants.BufferElements2Rec * Constants.BytesPerElement); mRecorder.startRecording(); isRecording = true; recordingThread = new Thread(new Runnable() { public void run() { writeAudioDataToFile(); } }, "AudioRecorder Thread"); recordingThread.start(); } private void writeAudioDataToFile() { // Write the output audio in byte FileOutputStream os = null; try { os = new FileOutputStream(mFileName); } catch (FileNotFoundException e) { e.printStackTrace(); } while (isRecording) { // gets the voice output from microphone to byte format mRecorder.read(sData, 0, Constants.BufferElements2Rec); try { // // writes the data to file from buffer // // stores the voice buffer byte bData[] = short2byte(sData); os.write(bData, 0, Constants.BufferElements2Rec * Constants.BytesPerElement); } catch (IOException e) { e.printStackTrace(); } } try { os.close(); } catch (IOException e) { e.printStackTrace(); } } 

To play the recorded sound code:

 private void startPlaying() { new Thread(new Runnable() { public void run() { try { File file = new File(mFileName); byte[] audioData = null; InputStream inputStream = new FileInputStream(mFileName); audioData = new byte[Constants.BufferElements2Rec]; mPlayer = new AudioTrack(AudioManager.STREAM_MUSIC, Constants.RECORDER_SAMPLERATE, AudioFormat.CHANNEL_OUT_MONO, Constants.RECORDER_AUDIO_ENCODING, Constants.BufferElements2Rec * Constants.BytesPerElement, AudioTrack.MODE_STREAM); final float duration = (float) file.length() / Constants.RECORDER_SAMPLERATE / 2; Log.i(TAG, "PLAYBACK AUDIO"); Log.i(TAG, String.valueOf(duration)); mPlayer.setPositionNotificationPeriod(Constants.RECORDER_SAMPLERATE / 10); mPlayer.setNotificationMarkerPosition(Math.round(duration * Constants.RECORDER_SAMPLERATE)); mPlayer.play(); int i = 0; while ((i = inputStream.read(audioData)) != -1) { try { mPlayer.write(audioData, 0, i); } catch (Exception e) { Log.e(TAG, "Exception: " + e.getLocalizedMessage()); } } } catch (FileNotFoundException fe) { Log.e(TAG, "File not found: " + fe.getLocalizedMessage()); } catch (IOException io) { Log.e(TAG, "IO Exception: " + io.getLocalizedMessage()); } } }).start(); } 

Constants defined in the Constants class:

 public class Constants { final static public int RECORDER_SAMPLERATE = 44100; final static public int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO; final static public int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT; final static public int BufferElements2Rec = 1024; // want to play 2048 (2K) since 2 bytes we use only 1024 final static public int BytesPerElement = 2; // 2 bytes in 16bit format } 

If I export the file as is, I convert it with Audacity and it plays. However, I need to export it in a format that can be played automatically.

I have seen the answers for implementing Lame and am currently working on it. I also found an answer to convert it using:

 private File rawToWave(final File rawFile, final String filePath) throws IOException { File waveFile = new File(filePath); 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, Constants.RECORDER_SAMPLERATE); // sample rate writeInt(output, Constants.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(bytes.array()); } finally { if (output != null) { output.close(); } } return waveFile; } 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)); } } 

But this, when exported, plays with the correct duration, but only with white noise.

Some of the answers I tried but failed to work:

Can anyone point out what is the best solution? Does it really implement the lame, or can it be done in a more direct way? If so, why does the sample code convert the file to white noise?

+5
source share
3 answers

You have most of the code correct. The only problem I see is the part in which you write PCM data to a WAV file. This should be pretty 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

This is a pretty simple use. Just name 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 does it all work

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

Cool tip

If you want to shift the pitch of your voice or make a voice changer application, all you need to do is increase / decrease the value of writeInt(output, 44100); // sample rate writeInt(output, 44100); // sample rate in your code. Reducing this means that the player will play it at a different speed, thereby changing the output step. Just a little more "good to know." :)

+11
source

Just to register, I decided that I needed to record the sound played by ordinary players using MediaRecorder instead of Audio Recorder.

To start recording:

  MediaRecorder mRecorder = new MediaRecorder(); 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 = new MediaPlayer(); mPlayer.setDataSource(Environment.getExternalStorageDirectory() .getAbsolutePath() + "/recording.3gp"); mPlayer.prepare(); mPlayer.start(); 
+2
source

I know that it’s too late, and you have your stuff working with MediaRecorder. But I thought about sharing my answer when it took me a while to find it. :)

When you record your audio signal, the read data is short from your AudioRecord object and then converted to bytes before being saved in a .pcm file.

Now when you write the .wav file, you are doing a short conversion again. This is not required. So, in your code, if you delete the next block and write rawData directly to the end of the .wav file. It will work fine.

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

Check the code code below, which you will receive after deleting the duplicate code block.

  writeInt(output, rawData.length); // subchunk 2 size // removed the duplicate short conversion output.write(rawData); 
+1
source

All Articles