将pcm转换为.wav - 嘈杂

时间:2014-11-12 12:18:32

标签: java android audio wav

我一直在尝试向我的应用程序添加一些代码,以将我记录的pcm文件转换为.wav文件。 我有编写标题的代码,但出于某种原因,当我通过音频播放器播放转换后的.wav文件时,您只能听到噪音。

这是我转换方法的代码:

public void writeWavHeader(){
    try {
        File pcmFile = new File(Environment.getExternalStorageDirectory() + File.separator + "TimeShiftRecorder" + File.separator + "Recording.pcm");
        File wavFile = new File(Environment.getExternalStorageDirectory() + File.separator + "TimeShiftRecorder" + File.separator + "Recording_test.wav");
        OutputStream os = new FileOutputStream(wavFile);
        BufferedOutputStream bos = new BufferedOutputStream(os);
        DataOutputStream out = new DataOutputStream(bos);
        FileInputStream fis = new FileInputStream(pcmFile);
        InputStream bis = new BufferedInputStream(fis);
        DataInputStream dis = new DataInputStream(bis);

        short mBitsPerSample = 16;
        long audioLength = fis.getChannel().size();
        long byteRate = sampleRate * bitsPerSample/8 * channels;
        short format = 1;
        long totalDataLen = audioLength + 36;
        long longSampleRate = 44100;
        byte byteBitsPerSample = (byte) bitsPerSample; // = 16
        byte[] header = new byte[44];

        header[0] = 'R';  // RIFF/WAVE header
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        header[4] = (byte) (totalDataLen & 0xff);
        header[5] = (byte) ((totalDataLen >> 8) & 0xff);
        header[6] = (byte) ((totalDataLen >> 16) & 0xff);
        header[7] = (byte) ((totalDataLen >> 24) & 0xff);
        header[8] = 'W';
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        header[12] = 'f';  // 'fmt ' chunk
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';
        header[16] = 16;  // 4 bytes: size of 'fmt ' chunk
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        header[20] = 1;  // format = 1
        header[21] = 0;
        header[22] = (byte) channels;
        header[23] = 0;
        header[24] = (byte) (longSampleRate & 0xff);
        header[25] = (byte) ((longSampleRate >> 8) & 0xff);
        header[26] = (byte) ((longSampleRate >> 16) & 0xff);
        header[27] = (byte) ((longSampleRate >> 24) & 0xff);
        header[28] = (byte) (byteRate & 0xff);
        header[29] = (byte) ((byteRate >> 8) & 0xff);
        header[30] = (byte) ((byteRate >> 16) & 0xff);
        header[31] = (byte) ((byteRate >> 24) & 0xff);
        header[32] = (byte) (bitsPerSample/8*channels);  // block align
        header[33] = 0;
        header[34] = byteBitsPerSample;  // bits per sample
        header[35] = 0;
        header[36] = 'd';
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        header[40] = (byte) (audioLength & 0xff);
        header[41] = (byte) ((audioLength >> 8) & 0xff);
        header[42] = (byte) ((audioLength >> 16) & 0xff);
        header[43] = (byte) ((audioLength >> 24) & 0xff);
        out.write(header, 0, 44);


        while (dis.available() > 0) {
            out.write(dis.read());
        }
        dis.close();
        out.close();
        //testHeader();

    }catch(IOException e){
        Log.v("recordService", "IOException");
    }

}

从我在网上找到的所有例子中,这个标题应该是正确的。

如果有人能帮助我解决我出错的地方,我将非常感激。

由于 科里:)

2 个答案:

答案 0 :(得分:0)

通常,WAV格式存储每个样本16位信息(跨两个字节的内存)。确保您阅读PCM并写入WAV并考虑到这一点。单个字节每个样本只能处理256个不同的值(通常每秒采集44100次),这将使8位格式的低保真度。相反,使用16位存储,您可以获得2 ^ 16个可能的不同值,这足以支持CD​​质量的音频。编写这样的代码时,它有两个程序:(1)将PCM转换为WAV,另一个(2)将WAV转换回PCM。通过这种方式,您可以使用(2)将已知良好的WAV读入PCM作为源数据,然后使用(1)输出WAV,然后比较两个WAV的相等性。

还要确保您读取/写入二进制文件而不是正常文件模式。

在您的情况下,由于WAV格式只是PCM数据,并且标题附加到文件的前面,如果您将PCM读取为二进制文件,请将标头输出到新的WAV文件,然后将内存缓冲区写为二进制文件对于WAV,你应该顺利去。

答案 1 :(得分:0)

这是一个完整的工作示例。

有关更多信息,请参见:http://soundfile.sapp.org/doc/WaveFormat/

/**
 * overload using AudioFormat
 *
 * @param input  raw PCM data
 *               limit of file size for wave file: < 2^(2*4) - 36 bytes (~4GB)
 * @param output file to encode to in wav format
 * @param format corresponding audioformat for PCM data
 * @throws IOException in event of an error between input/output files
 * @see <a href="http://soundfile.sapp.org/doc/WaveFormat/">soundfile.sapp.org/doc/WaveFormat</a>
 */
static public void PCMToWAV(File input, File output, AudioFormat format) throws IOException {
    int bitsPerSample;
    switch (format.getEncoding()) {
        case AudioFormat.ENCODING_PCM_8BIT:
            bitsPerSample = 8;
            break;
        case AudioFormat.ENCODING_PCM_FLOAT:
            bitsPerSample = 32;
            break;
        case AudioFormat.ENCODING_PCM_16BIT:
        default:
            bitsPerSample = 16;

    }
    PCMToWAV(input, output, format.getChannelCount(), format.getSampleRate(), bitsPerSample);
}

/**
 * @param input         raw PCM data
 *                      limit of file size for wave file: < 2^(2*4) - 36 bytes (~4GB)
 * @param output        file to encode to in wav format
 * @param channelCount  number of channels: 1 for mono, 2 for stereo, etc.
 * @param sampleRate    sample rate of PCM audio
 * @param bitsPerSample bits per sample, i.e. 16 for PCM16
 * @throws IOException in event of an error between input/output files
 * @see <a href="http://soundfile.sapp.org/doc/WaveFormat/">soundfile.sapp.org/doc/WaveFormat</a>
 */
static public void PCMToWAV(File input, File output, int channelCount, int sampleRate, int bitsPerSample) throws IOException {
    final int inputSize = (int) input.length();

    try (OutputStream encoded = new FileOutputStream(output)) {
        // WAVE RIFF header
        writeToOutput(encoded, "RIFF"); // chunk id
        writeToOutput(encoded, 36 + inputSize); // chunk size
        writeToOutput(encoded, "WAVE"); // format

        // SUB CHUNK 1 (FORMAT)
        writeToOutput(encoded, "fmt "); // subchunk 1 id
        writeToOutput(encoded, 16); // subchunk 1 size
        writeToOutput(encoded, (short) 1); // audio format (1 = PCM)
        writeToOutput(encoded, (short) channelCount); // number of channelCount
        writeToOutput(encoded, sampleRate); // sample rate
        writeToOutput(encoded, sampleRate * channelCount * bitsPerSample / 8); // byte rate
        writeToOutput(encoded, (short) (channelCount * bitsPerSample / 8)); // block align
        writeToOutput(encoded, (short) bitsPerSample); // bits per sample

        // SUB CHUNK 2 (AUDIO DATA)
        writeToOutput(encoded, "data"); // subchunk 2 id
        writeToOutput(encoded, inputSize); // subchunk 2 size
        copy(new FileInputStream(input), encoded);
    }
}


/**
 * Size of buffer used for transfer, by default
 */
private static final int TRANSFER_BUFFER_SIZE = 10 * 1024;

/**
 * Writes string in big endian form to an output stream
 *
 * @param output stream
 * @param data   string
 * @throws IOException
 */
public static void writeToOutput(OutputStream output, String data) throws IOException {
    for (int i = 0; i < data.length(); i++)
        output.write(data.charAt(i));
}

public static void writeToOutput(OutputStream output, int data) throws IOException {
    output.write(data >> 0);
    output.write(data >> 8);
    output.write(data >> 16);
    output.write(data >> 24);
}

public static void writeToOutput(OutputStream output, short data) throws IOException {
    output.write(data >> 0);
    output.write(data >> 8);
}

public static long copy(InputStream source, OutputStream output)
        throws IOException {
    return copy(source, output, TRANSFER_BUFFER_SIZE);
}

public static long copy(InputStream source, OutputStream output, int bufferSize) throws IOException {
    long read = 0L;
    byte[] buffer = new byte[bufferSize];
    for (int n; (n = source.read(buffer)) != -1; read += n) {
        output.write(buffer, 0, n);
    }
    return read;
}