java pcm to wav

眉间皱痕 提交于 2019-12-18 05:12:39

问题


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

Is there any suitable api or code for this?


回答1:


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



回答2:


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




回答3:


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



回答4:


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




回答5:


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.



来源:https://stackoverflow.com/questions/4440015/java-pcm-to-wav

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!