Search code examples
javaaudiowavpcm

How do I convert audio from one format to wav or raw in Java?


I am trying to figure out how to use the java.sound api to convert a WAV file to RAW/PCM format. I need to convert the WAV file from some given audio format to a RAW file encoded as follows:

  • 16 kHz
  • 16 bits
  • Mono
  • Signed
  • Little endian

Many of the posts I found here are for the other way around, how to go from RAW and get a WAV. Being new to audio, I was hoping someone could post an example showing how to do this.


Solution

  • The Java sound api does not convert to RAW audio as far as I know. It does convert to WAV, which is RAW audio with a 44-byte header. Once you understand this, you can break the problem down into 2 parts:

    1. Convert audio from any format to a WAV format.

    The code example below has only been tested from WAV to WAV (different audio format), but in theory the call to AudioSystem.getAudioInputStream(audioFormat, originalAudioStream); should find the appropriate codec if it has it.

    This method will convert audio bytes to the given format and produce a byte[] result as a WAV.

     private static final AudioFormat EXAMPLE_FORMAT = new AudioFormat(
            16_000,
            16,
            1,
            true,
            false
        );
    
        public byte[] formatAudioToWav(@NotNull final byte[] audioFileContent,
                                       @NotNull final AudioFormat audioFormat) throws
                                                                               IOException,
                                                                               UnsupportedAudioFileException {
    
            try (
                final AudioInputStream originalAudioStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(audioFileContent));
                final AudioInputStream formattedAudioStream = AudioSystem.getAudioInputStream(audioFormat, originalAudioStream);
                final AudioInputStream lengthAddedAudioStream = new AudioInputStream(formattedAudioStream, audioFormat, audioFileContent.length);
                final ByteArrayOutputStream convertedOutputStream = new ByteArrayOutputStream()
            ) {
                AudioSystem.write(lengthAddedAudioStream, AudioFileFormat.Type.WAVE, convertedOutputStream);
                return convertedOutputStream.toByteArray();
            }
        }
    

    2. Strip the header from the WAV file created in step 1.

       public byte[] formatWavToRaw(@NotNull final byte[] audioFileContent) {
            return Arrays.copyOfRange(audioFileContent, 44, audioFileContent.length);
        }
    

    Notes

    • The code has the advantage of properly closing all the streams, but it has the disadvantage of working with byte[] directly, so it won't work well with large files. It can be converted to pure streams, but notice you need the length of one of the streams in the call to new AudioInputStream(formattedAudioStream, audioFormat, audioFileContent.length);.
    • Another disadvantage is that even if the code is legible, there are 3 AudioInputStream's that need to be created/wrapped to make this work, which is somewhat convoluted. I was unable to find a better solution.

    Further Reading