private int writeWaveFile(InputStream in, WaveFileFormat waveFileFormat, OutputStream out)
      throws IOException {

    int bytesRead = 0;
    int bytesWritten = 0;
    InputStream fileStream = getFileStream(waveFileFormat, in);
    byte buffer[] = new byte[bisBufferSize];
    int maxLength = waveFileFormat.getByteLength();

    while ((bytesRead = fileStream.read(buffer)) >= 0) {

      if (maxLength > 0) {
        if (bytesRead < maxLength) {
          out.write(buffer, 0, bytesRead);
          bytesWritten += bytesRead;
          maxLength -= bytesRead;
        } else {
          out.write(buffer, 0, maxLength);
          bytesWritten += maxLength;
          maxLength = 0;
          break;
        }
      } else {
        out.write(buffer, 0, bytesRead);
        bytesWritten += bytesRead;
      }
    }

    return bytesWritten;
  }
  @Override
  public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out)
      throws IOException {
    Objects.requireNonNull(stream);
    Objects.requireNonNull(fileType);
    Objects.requireNonNull(out);

    // throws IllegalArgumentException if not supported
    WaveFileFormat waveFileFormat = (WaveFileFormat) getAudioFileFormat(fileType, stream);

    // first write the file without worrying about length fields
    FileOutputStream fos = new FileOutputStream(out); // throws IOException
    BufferedOutputStream bos = new BufferedOutputStream(fos, bisBufferSize);
    int bytesWritten = writeWaveFile(stream, waveFileFormat, bos);
    bos.close();

    // now, if length fields were not specified, calculate them,
    // open as a random access file, write the appropriate fields,
    // close again....
    if (waveFileFormat.getByteLength() == AudioSystem.NOT_SPECIFIED) {

      int dataLength = bytesWritten - waveFileFormat.getHeaderSize();
      int riffLength = dataLength + waveFileFormat.getHeaderSize() - 8;

      RandomAccessFile raf = new RandomAccessFile(out, "rw");
      // skip RIFF magic
      raf.skipBytes(4);
      raf.writeInt(big2little(riffLength));
      // skip WAVE magic, fmt_ magic, fmt_ length, fmt_ chunk, data magic
      raf.skipBytes(4 + 4 + 4 + WaveFileFormat.getFmtChunkSize(waveFileFormat.getWaveType()) + 4);
      raf.writeInt(big2little(dataLength));
      // that's all
      raf.close();
    }

    return bytesWritten;
  }
  private InputStream getFileStream(WaveFileFormat waveFileFormat, InputStream audioStream)
      throws IOException {
    // private method ... assumes audioFileFormat is a supported file type

    // WAVE header fields
    AudioFormat audioFormat = waveFileFormat.getFormat();
    int headerLength = waveFileFormat.getHeaderSize();
    int riffMagic = WaveFileFormat.RIFF_MAGIC;
    int waveMagic = WaveFileFormat.WAVE_MAGIC;
    int fmtMagic = WaveFileFormat.FMT_MAGIC;
    int fmtLength = WaveFileFormat.getFmtChunkSize(waveFileFormat.getWaveType());
    short wav_type = (short) waveFileFormat.getWaveType();
    short channels = (short) audioFormat.getChannels();
    short sampleSizeInBits = (short) audioFormat.getSampleSizeInBits();
    int sampleRate = (int) audioFormat.getSampleRate();
    int frameSizeInBytes = audioFormat.getFrameSize();
    int frameRate = (int) audioFormat.getFrameRate();
    int avgBytesPerSec = channels * sampleSizeInBits * sampleRate / 8;
    short blockAlign = (short) ((sampleSizeInBits / 8) * channels);
    int dataMagic = WaveFileFormat.DATA_MAGIC;
    int dataLength = waveFileFormat.getFrameLength() * frameSizeInBytes;
    int length = waveFileFormat.getByteLength();
    int riffLength = dataLength + headerLength - 8;

    byte header[] = null;
    ByteArrayInputStream headerStream = null;
    ByteArrayOutputStream baos = null;
    DataOutputStream dos = null;
    SequenceInputStream waveStream = null;

    AudioFormat audioStreamFormat = null;
    AudioFormat.Encoding encoding = null;
    InputStream codedAudioStream = audioStream;

    // if audioStream is an AudioInputStream and we need to convert, do it here...
    if (audioStream instanceof AudioInputStream) {
      audioStreamFormat = ((AudioInputStream) audioStream).getFormat();

      encoding = audioStreamFormat.getEncoding();

      if (AudioFormat.Encoding.PCM_SIGNED.equals(encoding)) {
        if (sampleSizeInBits == 8) {
          wav_type = WaveFileFormat.WAVE_FORMAT_PCM;
          // plug in the transcoder to convert from PCM_SIGNED to PCM_UNSIGNED
          codedAudioStream =
              AudioSystem.getAudioInputStream(
                  new AudioFormat(
                      AudioFormat.Encoding.PCM_UNSIGNED,
                      audioStreamFormat.getSampleRate(),
                      audioStreamFormat.getSampleSizeInBits(),
                      audioStreamFormat.getChannels(),
                      audioStreamFormat.getFrameSize(),
                      audioStreamFormat.getFrameRate(),
                      false),
                  (AudioInputStream) audioStream);
        }
      }
      if ((AudioFormat.Encoding.PCM_SIGNED.equals(encoding) && audioStreamFormat.isBigEndian())
          || (AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding)
              && !audioStreamFormat.isBigEndian())
          || (AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding)
              && audioStreamFormat.isBigEndian())) {
        if (sampleSizeInBits != 8) {
          wav_type = WaveFileFormat.WAVE_FORMAT_PCM;
          // plug in the transcoder to convert to PCM_SIGNED_LITTLE_ENDIAN
          codedAudioStream =
              AudioSystem.getAudioInputStream(
                  new AudioFormat(
                      AudioFormat.Encoding.PCM_SIGNED,
                      audioStreamFormat.getSampleRate(),
                      audioStreamFormat.getSampleSizeInBits(),
                      audioStreamFormat.getChannels(),
                      audioStreamFormat.getFrameSize(),
                      audioStreamFormat.getFrameRate(),
                      false),
                  (AudioInputStream) audioStream);
        }
      }
    }

    // Now push the header into a stream, concat, and return the new SequenceInputStream

    baos = new ByteArrayOutputStream();
    dos = new DataOutputStream(baos);

    // we write in littleendian...
    dos.writeInt(riffMagic);
    dos.writeInt(big2little(riffLength));
    dos.writeInt(waveMagic);
    dos.writeInt(fmtMagic);
    dos.writeInt(big2little(fmtLength));
    dos.writeShort(big2littleShort(wav_type));
    dos.writeShort(big2littleShort(channels));
    dos.writeInt(big2little(sampleRate));
    dos.writeInt(big2little(avgBytesPerSec));
    dos.writeShort(big2littleShort(blockAlign));
    dos.writeShort(big2littleShort(sampleSizeInBits));
    // $$fb 2002-04-16: Fix for 4636355: RIFF audio headers could be _more_ spec compliant
    if (wav_type != WaveFileFormat.WAVE_FORMAT_PCM) {
      // add length 0 for "codec specific data length"
      dos.writeShort(0);
    }

    dos.writeInt(dataMagic);
    dos.writeInt(big2little(dataLength));

    dos.close();
    header = baos.toByteArray();
    headerStream = new ByteArrayInputStream(header);
    waveStream = new SequenceInputStream(headerStream, new NoCloseInputStream(codedAudioStream));

    return waveStream;
  }
  /**
   * Returns the AudioFileFormat describing the file that will be written from this
   * AudioInputStream. Throws IllegalArgumentException if not supported.
   */
  private AudioFileFormat getAudioFileFormat(AudioFileFormat.Type type, AudioInputStream stream) {
    if (!isFileTypeSupported(type, stream)) {
      throw new IllegalArgumentException("File type " + type + " not supported.");
    }
    AudioFormat format = null;
    WaveFileFormat fileFormat = null;
    AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;

    AudioFormat streamFormat = stream.getFormat();
    AudioFormat.Encoding streamEncoding = streamFormat.getEncoding();

    float sampleRate;
    int sampleSizeInBits;
    int channels;
    int frameSize;
    float frameRate;
    int fileSize;

    int waveType = WaveFileFormat.WAVE_FORMAT_PCM;

    if (AudioFormat.Encoding.ALAW.equals(streamEncoding)
        || AudioFormat.Encoding.ULAW.equals(streamEncoding)) {

      encoding = streamEncoding;
      sampleSizeInBits = streamFormat.getSampleSizeInBits();
      if (streamEncoding.equals(AudioFormat.Encoding.ALAW)) {
        waveType = WaveFileFormat.WAVE_FORMAT_ALAW;
      } else {
        waveType = WaveFileFormat.WAVE_FORMAT_MULAW;
      }
    } else if (streamFormat.getSampleSizeInBits() == 8) {
      encoding = AudioFormat.Encoding.PCM_UNSIGNED;
      sampleSizeInBits = 8;
    } else {
      encoding = AudioFormat.Encoding.PCM_SIGNED;
      sampleSizeInBits = streamFormat.getSampleSizeInBits();
    }

    format =
        new AudioFormat(
            encoding,
            streamFormat.getSampleRate(),
            sampleSizeInBits,
            streamFormat.getChannels(),
            streamFormat.getFrameSize(),
            streamFormat.getFrameRate(),
            false); // WAVE is little endian

    if (stream.getFrameLength() != AudioSystem.NOT_SPECIFIED) {
      fileSize =
          (int) stream.getFrameLength() * streamFormat.getFrameSize()
              + WaveFileFormat.getHeaderSize(waveType);
    } else {
      fileSize = AudioSystem.NOT_SPECIFIED;
    }

    fileFormat =
        new WaveFileFormat(
            AudioFileFormat.Type.WAVE, fileSize, format, (int) stream.getFrameLength());

    return fileFormat;
  }