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

后端 未结 4 699
终归单人心
终归单人心 2020-12-14 09:29

I am currently developing an Android Application that has audio recording and playing. I am new to dealing with audio and I\'m having some trouble with encoding and formats.

相关标签:
4条回答
  • 2020-12-14 09:55

    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 = 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 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);
    
    0 讨论(0)
  • 2020-12-14 10:02

    Just to register, I solved my need of recording an audio playable in common 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();
    
    0 讨论(0)
  • 2020-12-14 10:04

    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. :)

    0 讨论(0)
  • 2020-12-14 10:08

    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[].

    0 讨论(0)
提交回复
热议问题