如何将.pcm文件转换为.wav或.mp3? [英] How to convert .pcm file to .wav or .mp3?

查看:385
本文介绍了如何将.pcm文件转换为.wav或.mp3?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在开发一个具有音频录制和播放功能的Android应用程序.我刚接触音频,在编码和格式方面遇到了一些麻烦.

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.

我可以在我的应用程序中记录和播放音频,但是在导出时,我无法再现音频.我发现的唯一方法是导出.pcm文件并使用Audacity进行转换.

I am able to record and play the audio in my application, but when exporting I am not able to reproduce the audio. The only way I found was exporting my .pcm file and converting using Audacity.

这是我录制音频的代码:

This is my code to record the audio is:

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 audio, the code is:

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类中定义的常量是:

The constants defined in a Constants class are:

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


}

如果按原样导出文件,则会使用Audacity将其转换并播放.但是,我确实需要将其导出为可以自动播放的格式.

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

我已经看到实现Lame的答案,目前正在研究中.我还找到了使用以下方法进行转换的答案:

I've seen answers to implement Lame and am currently working on it. I've 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 just white noise.

我尝试过但无法使用的一些答案:

Some of the answers that I've tried but wasn't able to work:

  • Android:Creating Wave file using Raw PCM, the wave file does not play
  • How to convert PCM raw data to mp3 file?
  • converting pcm file to mp3 using liblame in android

任何人都可以指出最佳解决方案是什么?它是真正实现la脚还是可以以更直接的方式完成?如果是这样,为什么代码示例将文件转换为白噪声?

Anyone can point out what is the best solution? Is it really implementing lame or can it be done on a more straight forward way? If so, why is the code sample converting the file to just white noise?

推荐答案

大多数代码正确无误.我唯一看到的问题是将PCM数据写入WAV文件的部分.这应该非常简单,因为WAV =元数据+ PCM(按此顺序).这应该起作用:

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

使用方法

使用非常简单.像这样称呼它:

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

这一切如何工作

如您所见,WAV标头是WAV和PCM文件格式之间的唯一区别.假设您正在录制16位PCM MONO音频(根据您的代码,您就是). rawToWave函数只是将标头整齐地添加到WAV文件中,以便音乐播放器知道打开文件时的期望,然后在标头之后,它仅从最后一位开始写入PCM数据.

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

如果您想改变声音的音调或制作语音转换器应用,您要做的就是增加/减小代码中writeInt(output, 44100); // sample rate的值.减小它会告诉演奏者以不同的速率演奏它,从而改变输出音高.只是有点额外的要知道"的事情. :)

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

这篇关于如何将.pcm文件转换为.wav或.mp3?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆