예제 #1
0
  public static WavFile newWavFile(
      File file, int numChannels, long numFrames, int validBits, long sampleRate)
      throws IOException, WavFileException {
    // Instantiate new Wavfile and initialise
    final WavFile wavFile = new WavFile();
    wavFile.file = file;
    wavFile.numChannels = numChannels;
    wavFile.numFrames = numFrames;
    wavFile.sampleRate = sampleRate;
    wavFile.bytesPerSample = (validBits + 7) / 8;
    wavFile.blockAlign = wavFile.bytesPerSample * numChannels;
    wavFile.validBits = validBits;

    // Sanity check arguments
    if ((numChannels < 1) || (numChannels > 65535)) {
      throw new WavFileException("Illegal number of channels, valid range 1 to 65536");
    }
    if (numFrames < 0) {
      throw new WavFileException("Number of frames must be positive");
    }
    if ((validBits < 2) || (validBits > 65535)) {
      throw new WavFileException("Illegal number of valid bits, valid range 2 to 65536");
    }
    if (sampleRate < 0) {
      throw new WavFileException("Sample rate must be positive");
    }

    // Create output stream for writing data
    wavFile.oStream = new FileOutputStream(file);

    // Calculate the chunk sizes
    final long dataChunkSize = wavFile.blockAlign * numFrames;
    long mainChunkSize =
        4
            + // Riff Type
            8
            + // Format ID and size
            16
            + // Format data
            8
            + // Data ID and size
            dataChunkSize;

    // Chunks must be word aligned, so if odd number of audio data bytes
    // adjust the main chunk size
    if ((dataChunkSize % 2) == 1) {
      mainChunkSize += 1;
      wavFile.wordAlignAdjust = true;
    } else {
      wavFile.wordAlignAdjust = false;
    }

    // Set the main chunk size
    putLE(RIFF_CHUNK_ID, wavFile.buffer, 0, 4);
    putLE(mainChunkSize, wavFile.buffer, 4, 4);
    putLE(RIFF_TYPE_ID, wavFile.buffer, 8, 4);

    // Write out the header
    wavFile.oStream.write(wavFile.buffer, 0, 12);

    // Put format data in buffer
    final long averageBytesPerSecond = sampleRate * wavFile.blockAlign;

    putLE(FMT_CHUNK_ID, wavFile.buffer, 0, 4); // Chunk ID
    putLE(16, wavFile.buffer, 4, 4); // Chunk Data Size
    putLE(1, wavFile.buffer, 8, 2); // Compression Code (Uncompressed)
    putLE(numChannels, wavFile.buffer, 10, 2); // Number of channels
    putLE(sampleRate, wavFile.buffer, 12, 4); // Sample Rate
    putLE(averageBytesPerSecond, wavFile.buffer, 16, 4); // Average Bytes Per Second
    putLE(wavFile.blockAlign, wavFile.buffer, 20, 2); // Block Align
    putLE(validBits, wavFile.buffer, 22, 2); // Valid Bits

    // Write Format Chunk
    wavFile.oStream.write(wavFile.buffer, 0, 24);

    // Start Data Chunk
    putLE(DATA_CHUNK_ID, wavFile.buffer, 0, 4); // Chunk ID
    putLE(dataChunkSize, wavFile.buffer, 4, 4); // Chunk Data Size

    // Write Format Chunk
    wavFile.oStream.write(wavFile.buffer, 0, 8);

    // Calculate the scaling factor for converting to a normalised double
    if (wavFile.validBits > 8) {
      // If more than 8 validBits, data is signed
      // Conversion required multiplying by magnitude of max positive value
      wavFile.floatOffset = 0;
      wavFile.floatScale = Long.MAX_VALUE >> (64 - wavFile.validBits);
    } else {
      // Else if 8 or less validBits, data is unsigned
      // Conversion required dividing by max positive value
      wavFile.floatOffset = 1;
      wavFile.floatScale = 0.5 * ((1 << wavFile.validBits) - 1);
    }

    // Finally, set the IO State
    wavFile.bufferPointer = 0;
    wavFile.bytesRead = 0;
    wavFile.frameCounter = 0;
    wavFile.ioState = IOState.WRITING;

    return wavFile;
  }
예제 #2
0
  public static WavFile openWavFile(File file) throws IOException, WavFileException {
    // Instantiate new Wavfile and store the file reference
    final WavFile wavFile = new WavFile();
    wavFile.file = file;

    // Create a new file input stream for reading file data
    wavFile.iStream = new FileInputStream(file);

    // Read the first 12 bytes of the file
    int bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 12);
    if (bytesRead != 12) {
      throw new WavFileException("Not enough wav file bytes for header");
    }

    // Extract parts from the header
    final long riffChunkID = getLE(wavFile.buffer, 0, 4);
    long chunkSize = getLE(wavFile.buffer, 4, 4);
    final long riffTypeID = getLE(wavFile.buffer, 8, 4);

    // Check the header bytes contains the correct signature
    if (riffChunkID != RIFF_CHUNK_ID) {
      throw new WavFileException("Invalid Wav Header data, incorrect riff chunk ID");
    }
    if (riffTypeID != RIFF_TYPE_ID) {
      throw new WavFileException("Invalid Wav Header data, incorrect riff type ID");
    }

    // Check that the file size matches the number of bytes listed in header
    if (file.length() != (chunkSize + 8)) {
      throw new WavFileException(
          "Header chunk size (" + chunkSize + ") does not match file size (" + file.length() + ")");
    }

    boolean foundFormat = false;
    boolean foundData = false;

    // Search for the Format and Data Chunks
    while (true) {
      // Read the first 8 bytes of the chunk (ID and chunk size)
      bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 8);
      if (bytesRead == -1) {
        throw new WavFileException("Reached end of file without finding format chunk");
      }
      if (bytesRead != 8) {
        throw new WavFileException("Could not read chunk header");
      }

      // Extract the chunk ID and Size
      final long chunkID = getLE(wavFile.buffer, 0, 4);
      chunkSize = getLE(wavFile.buffer, 4, 4);

      // Word align the chunk size
      // chunkSize specifies the number of bytes holding data. However,
      // the data should be word aligned (2 bytes) so we need to calculate
      // the actual number of bytes in the chunk
      long numChunkBytes = ((chunkSize % 2) == 1) ? chunkSize + 1 : chunkSize;

      if (chunkID == FMT_CHUNK_ID) {
        // Flag that the format chunk has been found
        foundFormat = true;

        // Read in the header info
        bytesRead = wavFile.iStream.read(wavFile.buffer, 0, 16);

        // Check this is uncompressed data
        final int compressionCode = (int) getLE(wavFile.buffer, 0, 2);
        if (compressionCode != 1) {
          throw new WavFileException("Compression Code " + compressionCode + " not supported");
        }

        // Extract the format information
        wavFile.numChannels = (int) getLE(wavFile.buffer, 2, 2);
        wavFile.sampleRate = getLE(wavFile.buffer, 4, 4);
        wavFile.blockAlign = (int) getLE(wavFile.buffer, 12, 2);
        wavFile.validBits = (int) getLE(wavFile.buffer, 14, 2);

        if (wavFile.numChannels == 0) {
          throw new WavFileException("Number of channels specified in header is equal to zero");
        }
        if (wavFile.blockAlign == 0) {
          throw new WavFileException("Block Align specified in header is equal to zero");
        }
        if (wavFile.validBits < 2) {
          throw new WavFileException("Valid Bits specified in header is less than 2");
        }
        if (wavFile.validBits > 64) {
          throw new WavFileException(
              "Valid Bits specified in header is greater than 64, this is greater than a long can hold");
        }

        // Calculate the number of bytes required to hold 1 sample
        wavFile.bytesPerSample = (wavFile.validBits + 7) / 8;
        if ((wavFile.bytesPerSample * wavFile.numChannels) != wavFile.blockAlign) {
          throw new WavFileException(
              "Block Align does not agree with bytes required for validBits and number of channels");
        }

        // Account for number of format bytes and then skip over
        // any extra format bytes
        numChunkBytes -= 16;
        if (numChunkBytes > 0) {
          wavFile.iStream.skip(numChunkBytes);
        }
      } else if (chunkID == DATA_CHUNK_ID) {
        // Check if we've found the format chunk,
        // If not, throw an exception as we need the format information
        // before we can read the data chunk
        if (foundFormat == false) {
          throw new WavFileException("Data chunk found before Format chunk");
        }

        // Check that the chunkSize (wav data length) is a multiple of the
        // block align (bytes per frame)
        if ((chunkSize % wavFile.blockAlign) != 0) {
          throw new WavFileException("Data Chunk size is not multiple of Block Align");
        }

        // Calculate the number of frames
        wavFile.numFrames = chunkSize / wavFile.blockAlign;

        // Flag that we've found the wave data chunk
        foundData = true;

        break;
      } else {
        // If an unknown chunk ID is found, just skip over the chunk data
        wavFile.iStream.skip(numChunkBytes);
      }
    }

    // Throw an exception if no data chunk has been found
    if (foundData == false) {
      throw new WavFileException("Did not find a data chunk");
    }

    // Calculate the scaling factor for converting to a normalised double
    if (wavFile.validBits > 8) {
      // If more than 8 validBits, data is signed
      // Conversion required dividing by magnitude of max negative value
      wavFile.floatOffset = 0;
      wavFile.floatScale = 1 << (wavFile.validBits - 1);
    } else {
      // Else if 8 or less validBits, data is unsigned
      // Conversion required dividing by max positive value
      wavFile.floatOffset = -1;
      wavFile.floatScale = 0.5 * ((1 << wavFile.validBits) - 1);
    }

    wavFile.bufferPointer = 0;
    wavFile.bytesRead = 0;
    wavFile.frameCounter = 0;
    wavFile.ioState = IOState.READING;

    return wavFile;
  }