java pcm 转 wav

发布于 2024-10-08 04:58:04 字数 56 浏览 0 评论 0原文

我有一个 pcm 文件,我想将其转换为 wav 文件。

有没有合适的api或代码?

I have a pcm file, and I want to convert it to a wav file.

Is there any suitable api or code for this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

許願樹丅啲祈禱 2024-10-15 04:58:04

这是我的代码

/**
 * Write PCM data as WAV file
 * @param os  Stream to save file to
 * @param pcmdata  8 bit PCMData
 * @param srate  Sample rate - 8000, 16000, etc.
 * @param channel Number of channels - Mono = 1, Stereo = 2, etc..
 * @param format Number of bits per sample (16 here)
 * @throws IOException
 */
public void PCMtoFile(OutputStream os, short[] pcmdata, int srate, int channel, int format) throws IOException {
    byte[] header = new byte[44];
    byte[] data = get16BitPcm(pcmdata);

    long totalDataLen = data.length + 36;
    long bitrate = srate * channel * format;

    header[0] = 'R'; 
    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'; 
    header[13] = 'm';
    header[14] = 't';
    header[15] = ' ';
    header[16] = (byte) format; 
    header[17] = 0;
    header[18] = 0;
    header[19] = 0;
    header[20] = 1; 
    header[21] = 0;
    header[22] = (byte) channel; 
    header[23] = 0;
    header[24] = (byte) (srate & 0xff);
    header[25] = (byte) ((srate >> 8) & 0xff);
    header[26] = (byte) ((srate >> 16) & 0xff);
    header[27] = (byte) ((srate >> 24) & 0xff);
    header[28] = (byte) ((bitrate / 8) & 0xff);
    header[29] = (byte) (((bitrate / 8) >> 8) & 0xff);
    header[30] = (byte) (((bitrate / 8) >> 16) & 0xff);
    header[31] = (byte) (((bitrate / 8) >> 24) & 0xff);
    header[32] = (byte) ((channel * format) / 8); 
    header[33] = 0;
    header[34] = 16; 
    header[35] = 0;
    header[36] = 'd';
    header[37] = 'a';
    header[38] = 't';
    header[39] = 'a';
    header[40] = (byte) (data.length  & 0xff);
    header[41] = (byte) ((data.length >> 8) & 0xff);
    header[42] = (byte) ((data.length >> 16) & 0xff);
    header[43] = (byte) ((data.length >> 24) & 0xff);

    os.write(header, 0, 44);
    os.write(data);
    os.close();
}

编辑:2016-01-11

public byte[] get16BitPcm(short[] data) {
    byte[] resultData = new byte[2 * data.length];
    int iter = 0;
    for (double sample : data) {
        short maxSample = (short)((sample * Short.MAX_VALUE));
        resultData[iter++] = (byte)(maxSample & 0x00ff);
        resultData[iter++] = (byte)((maxSample & 0xff00) >>> 8);
    }
    return resultData;
}

it's My Code

/**
 * Write PCM data as WAV file
 * @param os  Stream to save file to
 * @param pcmdata  8 bit PCMData
 * @param srate  Sample rate - 8000, 16000, etc.
 * @param channel Number of channels - Mono = 1, Stereo = 2, etc..
 * @param format Number of bits per sample (16 here)
 * @throws IOException
 */
public void PCMtoFile(OutputStream os, short[] pcmdata, int srate, int channel, int format) throws IOException {
    byte[] header = new byte[44];
    byte[] data = get16BitPcm(pcmdata);

    long totalDataLen = data.length + 36;
    long bitrate = srate * channel * format;

    header[0] = 'R'; 
    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'; 
    header[13] = 'm';
    header[14] = 't';
    header[15] = ' ';
    header[16] = (byte) format; 
    header[17] = 0;
    header[18] = 0;
    header[19] = 0;
    header[20] = 1; 
    header[21] = 0;
    header[22] = (byte) channel; 
    header[23] = 0;
    header[24] = (byte) (srate & 0xff);
    header[25] = (byte) ((srate >> 8) & 0xff);
    header[26] = (byte) ((srate >> 16) & 0xff);
    header[27] = (byte) ((srate >> 24) & 0xff);
    header[28] = (byte) ((bitrate / 8) & 0xff);
    header[29] = (byte) (((bitrate / 8) >> 8) & 0xff);
    header[30] = (byte) (((bitrate / 8) >> 16) & 0xff);
    header[31] = (byte) (((bitrate / 8) >> 24) & 0xff);
    header[32] = (byte) ((channel * format) / 8); 
    header[33] = 0;
    header[34] = 16; 
    header[35] = 0;
    header[36] = 'd';
    header[37] = 'a';
    header[38] = 't';
    header[39] = 'a';
    header[40] = (byte) (data.length  & 0xff);
    header[41] = (byte) ((data.length >> 8) & 0xff);
    header[42] = (byte) ((data.length >> 16) & 0xff);
    header[43] = (byte) ((data.length >> 24) & 0xff);

    os.write(header, 0, 44);
    os.write(data);
    os.close();
}

EDIT: 2016-01-11

public byte[] get16BitPcm(short[] data) {
    byte[] resultData = new byte[2 * data.length];
    int iter = 0;
    for (double sample : data) {
        short maxSample = (short)((sample * Short.MAX_VALUE));
        resultData[iter++] = (byte)(maxSample & 0x00ff);
        resultData[iter++] = (byte)((maxSample & 0xff00) >>> 8);
    }
    return resultData;
}
清风挽心 2024-10-15 04:58:04

这应该很简单,因为 WAV = 元数据 + PCM(按此顺序)。这应该可行:

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

如何使用

使用起来非常简单。就像这样称呼它:

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 数据。

酷提示

如果你想改变你的音调,或者制作一个变声器应用程序,你所要做的就是增加/减少writeInt(output, 44100);的值。 // 代码中的采样率。减小它会告诉玩家以不同的速率播放,从而改变输出音调。只是一点额外的“很高兴知道”的事情。 :)

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

梦醒时光 2024-10-15 04:58:04

我还不能发表评论,但请注意 devflow 答案中的 get16BitPcm 方法[奇怪]缩放输入数据。如果您已经有 16 位 pcm 数据要写入 wav 文件,则该方法应如下所示:

public byte[] get16BitPcm(short[] data) {
    byte[] resultData = new byte[2 * data.length];
    int iter = 0;
    for (short sample : data) {
        resultData[iter++] = (byte)(sample & 0x00ff);
        resultData[iter++] = (byte)((sample & 0xff00) >>> 8);
    }
    return resultData;
}

I cannot yet leave comments, but be aware that the get16BitPcm method in devflow's answer is [oddly] scaling the input data. If you already have 16 bit pcm data to write to the wav file, the method should look something like this:

public byte[] get16BitPcm(short[] data) {
    byte[] resultData = new byte[2 * data.length];
    int iter = 0;
    for (short sample : data) {
        resultData[iter++] = (byte)(sample & 0x00ff);
        resultData[iter++] = (byte)((sample & 0xff00) >>> 8);
    }
    return resultData;
}
双马尾 2024-10-15 04:58:04

我知道一个叫做“OperateWav”的东西,我在第一次实习期间在我的第一个项目中用它来开发一个转换器(linux c/c++)。我不确定这个文件本身是否存在以及它是否支持java。实际上wav文件很简单在 pcm 原始数据上添加 wav 格式标头...

I know one called "OperateWav" that I used to develop a convertor(linux c/c++) in my first project during my first internship.I am not sure if this one exists per se and whether it supports java.Actually wav file is simply adding a wav format header on pcm raw data...

同展鸳鸯锦 2024-10-15 04:58:04

此资源,WAVE PCM 声音文件format,帮助我将 PCM 数据解析为 WAVE。我已经基于它构建了一个库,它对我来说工作得很好。

This resource, WAVE PCM soundfile format, helped me to parse PCM data to WAVE. I've built a library based on it and it works fine for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文