wav file icon

A friend of mine produces podcasts for a living and she needed help with a corrupted WAV file. What I think happened is that the recording device malfunctioned or it ran out of disk space, so it never completed the WAV file header information. Without the correct header info it couldn't be read properly.

After inspecting the file she discovered that the incomplete parts of the header were the file size and the data size (it blew me away how much she'd learned about the WAV header format in such a short period). She had recovered all the data from her flash drive and all I needed to do was search for the beginning of the header information and rebuild the file from that point onwards with correct header info.

WAV files have a mix of big and little endian encoding, so there were a few moments of confusion. But eventually I was able to salvage over an hour of her interview. Feels great to use my coding skills to help out a friend!

I had to use a lot of different resources to really understand the header information. Here are a few of the most helpful:

I also used a few different tools to help convert hexadecimal to decimal and to look at the file's hexadecimal encoding:

I probably didn't need to write a program to do this and could have used hex fiend's editing options, but sometimes life is more fun if you just throw yourself into some code!!

Here's the Java I used to clean up the file:

import java.io.*;  
import java.io.FileOutputStream;  
import java.nio.ByteBuffer;  
import java.nio.ByteOrder;


public class Main {

  public static void main(String[] args) throws java.io.IOException {
    BufferedInputStream in = null;
    BufferedOutputStream out = null;
    File file = new File(args[0]);
    int fileSize = (int)file.length();

    try {
      in = new BufferedInputStream(new FileInputStream(args[0]));
      out = new BufferedOutputStream(new FileOutputStream(args[1]));
      byte[] ioBuf = new byte[4];
      int bytesRead;
      boolean bFoundHeader = false;
      while ((bytesRead = in.read(ioBuf)) != -1) {

        // int representation of "RIFF", faster than converting 
        // the value to string every time
        int num = ioBuf[0] << 24 |
                (ioBuf[1] & 0xff) << 16 |
                (ioBuf[2] & 0xff) << 8 |
                (ioBuf[3] & 0xff);

        // if RIFF found
        if (1380533830 == num)
        {
          // write RIFF, bytes 1-4
          out.write(ioBuf, 0, bytesRead);

          // write file size, bytes 5-8
          ByteBuffer bb = ByteBuffer.allocate(4);
          bb.order(ByteOrder.LITTLE_ENDIAN);
          bb.putInt(fileSize - 8);
          bb.flip();
          in.read(ioBuf, 0, 4);
          out.write(bb.array(), 0, 4);

          // write header contents, bytes 9-40
          byte[] ioBufHeader = new byte[40-8];
          in.read(ioBufHeader, 0, ioBufHeader.length);
          out.write(ioBufHeader, 0, ioBufHeader.length);

          // write data size, bytes 41-44
          bb = ByteBuffer.allocate(4);
          bb.order(ByteOrder.LITTLE_ENDIAN);
          bb.putInt(fileSize - 44);
          bb.flip();
          out.write(bb.array(), 0, 4);

          bFoundHeader = true;
        }
        else if (bFoundHeader)
        {
          out.write(ioBuf, 0, bytesRead);
        }

        fileSize -= bytesRead;
      }
    } catch (FileNotFoundException f) {
      System.out.println(f.getLocalizedMessage());
    } finally {
      if (in != null) {
        in.close();
      }
      if (out != null) {
        out.flush();
        out.close();
      }
    }
    System.out.println(args[0]);
  }
}