This discussion is archived
4 Replies Latest reply: Aug 9, 2012 4:09 AM by gimbal2 RSS

Large Wav files

953238 Newbie
Currently Being Moderated
Hi,

I'm trying to open a large (3 gigabyte) wav file using

AudioInputStream audioStream = AudioSystem.getAudioInputStream(file)

The size of the file is returned as negative and it's impossible to read from the file using

bytesRead = audioStream.read(byteArray, 0, blockSize);

I've experience of unpacking wav files byte by byte (in C). I assume that this issue is being caused by the unsigned 32 bit integer giving the file size in the wav header being read by Java as a signed integer, which then of course exceeds the maximum value for an signed int and wraps to become a negative number.

Clearly the problem could be fixed in the part of Java which reads the actual wav file header with a few lines of code which read the size, convert to long, check it's > 0 and if it isn't, then add 2^32 (to the long, which won't wrap).

Has anyone found a simple work around ? My options seem to be either to work out how to fix the Java source myself (which I'm reluctant to dive into - life is short) or to write my own wav file reader, which will lose me a lot of the friendly functionality of javax.sound and require me to rewrite a lot of existing code.

Many thanks for any assistance.

Douglas.
  • 1. Re: Large Wav files
    gimbal2 Guru
    Currently Being Moderated
    950235 wrote:
    Has anyone found a simple work around ? My options seem to be either to work out how to fix the Java source myself (which I'm reluctant to dive into - life is short) or to write my own wav file reader, which will lose me a lot of the friendly functionality of javax.sound and require me to rewrite a lot of existing code.
    Option 3: don't reinvent the wheel but find a third party solution. In game dev environments people like Paul's sound system:

    http://www.paulscode.com/forum/index.php?topic=4.0

    Even if you're not creating a game that needs 3D sound capabilities, I'm pretty sure you can apply the framework for your simpler needs.

    Option 4: find a pre-existing piece of C/C++ WAV loading code and port it to Java


    Note that manually loading a wav would not mean you have to "lose" java sound, the only thing is that you wouldn't be using is the part of java sound that loads wavs.
  • 2. Re: Large Wav files
    953238 Newbie
    Currently Being Moderated
    Thanks,
    Pauls code however is effectively just a wrapper around the same Java functions that I'm using and will therefore fail in exactly the same way with files > 2Gigabytes.
    Looks like I'm about to reinvent the wheel !
  • 3. Re: Large Wav files
    953238 Newbie
    Currently Being Moderated
    For anyone following this thread, I came up with the following solution:
    I rewrote the static function AudioInputStream Audiosystem.getAudioInputStream() so that it opens wav files using my own WavFileInputStream class. This unpacks the wavfile header structures and makes checks to ensure that if the data size has wrapped to a negative number, it sorts it out. This solution will work with the rest of the Java audio system for all wav file formats except mono 8 bit files. This is because after the header has been read, the number of samples rather than the number of bytes is passed between certain functions. Since with stereo data or 16 bit data the number of samples will be half or less than the total data length, it becomes impossible for that number to exceed the maximum signed integer value.

    public class PamAudioSystem {

         public static AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException {
              if (isWavFile(file)) {
                   return WavFileInputStream.openInputStream(file);
              }
              else {
                   return AudioSystem.getAudioInputStream(file);
              }
         }

         private static boolean isWavFile(File file) {
              String name = file.getName();
              if (name.length() < 4) {
                   return false;
              }
              String end = name.substring(name.length()-4).toLowerCase();
              return (end.equals(".wav"));
         }
         
    }
    And
    /**
    * Wav file input stream which will work for large wav files (>2 Gigabytes) which
    * fail with the standard JAva classes since the data chunk size gets read as a
    * signed integer which then ends up < 0.
    * <p>
    * This class should work with files of up to 4 Gigabytes (or 2^32 bytes). The only
    * exception would be mono 8 bit files. This is because various parts of the
    * super class (e.g. the available() function)
    * use int for a sample number variable. With stereo or 16 bit files, the
    * sample number can never exceed 2^31, since will be half or a quarter of the overall
    * file size (minus the header). However, for mono 8 bit files, the sample number can still reach
    * almost 2^32 which will mess up any parts of the code using a 32 bit sample number.
    * @author Doug Gillespie
    *
    */
    public class WavFileInputStream extends AudioInputStream {

         private WavHeader wavHeader;
         
         private WavFileInputStream(WavHeader wavHeader, InputStream stream, AudioFormat format,
                   long length) {
              super(stream, format, length);
              this.wavHeader = wavHeader;
         }

         public static WavFileInputStream openInputStream(File file) throws UnsupportedAudioFileException, IOException
         {
              /*
              * Read the wav header and from the information in the header,
              * create and Audioformat object and also get the file length in frames.
              */
              WavHeader wavHeader = new WavHeader();
              WindowsFile windowsFile = new WindowsFile(file, "r");
              if (wavHeader.readHeader(windowsFile) == false) {
                   throw new UnsupportedAudioFileException("Unsupprted wav file format in " + file.getName());
              }
              long nFrames = wavHeader.getDataSize() / wavHeader.getBlockAlign();
              AudioFormat audioFormat = new AudioFormat(Encoding.PCM_SIGNED,
                        wavHeader.getSampleRate(), wavHeader.getBitsPerSample(), wavHeader.getNChannels(),
                        wavHeader.getBlockAlign(), wavHeader.getSampleRate(), false);
              /**
              * Close the windows file since it's not compatible with the rest of the audio system
              * and pass over a more standard input stream to be read.
              */
              windowsFile.close();
              
              BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file));
              inputStream.skip(wavHeader.getDataStart());
              
              return new WavFileInputStream(wavHeader, inputStream, audioFormat, nFrames);
         }

         /**
         * Get additional header information from the wav file.
         * @return the wavHeader
         */
         public WavHeader getWavHeader() {
              return wavHeader;
         }

    }
    public class WavHeader {

         private short fmtTag, nChannels = 0, blockAlign = 0, bitsPerSample = 0;
         
         private int sampleRate = 0, bytesPerSec = 0;
         
         private boolean headerOk = false;

         private long dataStart;
         
         private long dataSize;
         
         private ArrayList<WavHeadChunk> wavHeadChunks = new ArrayList<WavHeadChunk>();
         
         public boolean readHeader(WindowsFile windowsWavFile) {

              headerOk = false;
              
              try {
                   windowsWavFile.seek(0);
              } catch (IOException e1) {
                   // TODO Auto-generated catch block
                   e1.printStackTrace();
                   return false;
              }
              char riff[] = new char[4];
              long totalSize;
              char wave[] = new char[4];
              char dataHead[];
              char testChars[];
              String testString;
              long filePointer;
              int intDataSize;
              int fmtSize;
              long fmtEnd;
              int chunkSize = 0;
              byte[] headChunk;
              try {
                   riff = read4Chars(windowsWavFile);
                   totalSize = windowsWavFile.readWinInt();
                   totalSize = checkUintProblem(totalSize);
                   wave = read4Chars(windowsWavFile);
                   while (true) {
                        filePointer = windowsWavFile.getFilePointer();
                        testChars = read4Chars(windowsWavFile);
                        testString = new String(testChars);
                        if (testString.equals("fmt ")) {
                             break;
                        }
                        else {
                             chunkSize = windowsWavFile.readWinInt();
                        }
                        headChunk = new byte[chunkSize];
                        windowsWavFile.read(headChunk);
                        wavHeadChunks.add(new WavHeadChunk(testString, headChunk));
    //                    windowsWavFile.seek(windowsWavFile.getFilePointer() + chunkSize);
                   }
                   // should now be at the start of the format section
                   fmtSize = windowsWavFile.readWinInt();
                   fmtEnd = windowsWavFile.getFilePointer() + fmtSize;
                   fmtTag = (short) windowsWavFile.readWinShort();
                   nChannels = (short) windowsWavFile.readWinShort();
                   sampleRate = windowsWavFile.readWinInt();
                   bytesPerSec = windowsWavFile.readWinInt();
                   blockAlign = (short) windowsWavFile.readWinShort();
                   bitsPerSample = (short) windowsWavFile.readWinShort();
                   windowsWavFile.seek(fmtEnd);
                   dataHead = read4Chars(windowsWavFile);
                   dataSize = windowsWavFile.readWinInt();
                   dataSize = checkUintProblem(dataSize);
                   dataStart = windowsWavFile.getFilePointer();
                   
                   
              } catch (IOException e) {
                   // TODO Auto-generated catch block
                   e.printStackTrace();
                   return false;
              }
              headerOk = true;
              return true;
         }
         
         private long checkUintProblem(long totalSize) {
              if (totalSize < 0) {
                   totalSize += (1L<<32);
              }
              return totalSize;
         }

         private char[] read4Chars(WindowsFile wFile) throws IOException {
              char[] chars = new char[4];
              for (int i = 0; i < 4; i++) {
                   chars[i] = (char) wFile.readByte();
              }
              return chars;
         }

         /**
         * @return the fmtTag
         */
         public short getFmtTag() {
              return fmtTag;
         }

         /**
         * @return the nChannels
         */
         public short getNChannels() {
              return nChannels;
         }

         /**
         * @return the blockAlign
         */
         public short getBlockAlign() {
              return blockAlign;
         }

         /**
         * @return the bitsPerSample
         */
         public short getBitsPerSample() {
              return bitsPerSample;
         }

         /**
         * @return the sampleRate
         */
         public int getSampleRate() {
              return sampleRate;
         }

         /**
         * @return the bytesPerSec
         */
         public int getBytesPerSec() {
              return bytesPerSec;
         }

         /**
         * @return the headerOk
         */
         public boolean isHeaderOk() {
              return headerOk;
         }

         /**
         *
         * @return byte number for the start of the data.
         */
         public long getDataStart() {
              return dataStart;
         }

         /**
         * @return the dataSize
         */
         public long getDataSize() {
              return dataSize;
         }
         
         /**
         * Get the number of additional chunks in the wav header.
         * @return the number of additional chunks in the wav header.
         */
         public int getNumHeadChunks() {
              return wavHeadChunks.size();
         }
         
         /**
         * Get a chunk from the wav header.
         * @param iChunk chunk number
         * @return Chunk read from wav header.
         */
         public WavHeadChunk getHeadChunk(int iChunk) {
              return wavHeadChunks.get(iChunk);
         }
    }
  • 4. Re: Large Wav files
    gimbal2 Guru
    Currently Being Moderated
    Good of you to post back what worked for you - to finalize it why don't you repost the (formatted) code between \
     tags to make it readable and keep the forum from trying to interpret it.                                                                                                                                                                                                                                                                                                                                                                                                

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points