Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java pcm to wav

Tags:

java

wav

pcm

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

Is there any suitable api or code for this?

like image 508
codingforeverX Avatar asked Dec 12 '22 17:12

codingforeverX


2 Answers

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;
}
like image 93
devflow Avatar answered Dec 24 '22 19:12

devflow


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

like image 30
Advait Saravade Avatar answered Dec 24 '22 18:12

Advait Saravade