Search code examples
javaaudioencodingwavpcm

Trying to write raw PCM to WAV and only getting header


I am currently using this code:

File wavFile=new File("tmp"+File.separator+"recordings"+File.separator+uuid.toString()+".wav");
try{
    FileInputStream pcmInputStream=new FileInputStream(file);
    FileOutputStream wavOutputStream=new FileOutputStream(wavFile);
    AudioSystem.write(new AudioInputStream(new ByteArrayInputStream(IOUtils.toByteArray(pcmInputStream)),
    new AudioFormat(48000,16,2,true,
    true),IOUtils.toByteArray(pcmInputStream).length/4),
    AudioFileFormat.Type.WAVE,wavOutputStream);
    wavOutputStream.flush();
    wavOutputStream.close();
    pcmInputStream.close();
    fileOutputStream.close();
}catch(IOException e){
    e.printStackTrace();
}

Having checked, I can confirm that the PCM has 30 seconds of data and is approximately 4.7MB. This writes the .wav file however it is only 44 bytes and not playable, I reckon that this 44 bytes is the RIFF header but am unsure of how to solve it. I have tried using different lengths and different combinations of File/ByteArray OutputStreams.


Solution

  • When creating the AudioInputStream, you call IOUtils.toByteArray(pcmInputStream) twice, and on the second call, the input stream is already advanced to the end, so toByteArray returns an empty array. This results in 0 getting passed as the argument to the AudioInputStream constructor's length.

    new AudioInputStream(
        new ByteArrayInputStream( IOUtils.toByteArray(pcmInputStream) ),
        new AudioFormat(48000,16,2,true,true),
        ( IOUtils.toByteArray(pcmInputStream) ).length/4
    )
    

    You should save the byte array to a temporary variable instead of trying to call toByteArray twice on the same input stream.