150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert/*
250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Copyright (C) 2011 The Android Open Source Project
350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert *
450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License"); you may not
550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * use this file except in compliance with the License. You may obtain a copy of
650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * the License at
750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert *
850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0
950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert *
1050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Unless required by applicable law or agreed to in writing, software
1150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * License for the specific language governing permissions and limitations under
1450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * the License.
1550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */
1650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertpackage android.speech.tts;
1750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
1850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.media.AudioFormat;
19563fd3ae1b9d388f883b77b98a641a57bbdb6aedNarayan Kamathimport android.os.FileUtils;
2050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.util.Log;
2150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
2250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport java.io.File;
235acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniakimport java.io.FileOutputStream;
2450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport java.io.IOException;
2550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport java.io.RandomAccessFile;
2650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport java.nio.ByteBuffer;
2750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport java.nio.ByteOrder;
285acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniakimport java.nio.channels.FileChannel;
2950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
3050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert/**
3150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Speech synthesis request that writes the audio to a WAV file.
3250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */
33e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamathclass FileSynthesisCallback extends AbstractSynthesisCallback {
3450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
3550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final String TAG = "FileSynthesisRequest";
3650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final boolean DBG = false;
3750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
3871e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    private static final int MAX_AUDIO_BUFFER_SIZE = 8192;
3971e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert
4050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final int WAV_HEADER_LENGTH = 44;
4150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private static final short WAV_FORMAT_PCM = 0x0001;
4250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
4350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private final Object mStateLock = new Object();
445acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak
4550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private int mSampleRateInHz;
4650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private int mAudioFormat;
4750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private int mChannelCount;
485acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak
495acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak    private FileChannel mFileChannel;
505acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak
515acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak    private boolean mStarted = false;
5250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private boolean mStopped = false;
53360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    private boolean mDone = false;
5450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
555acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak    FileSynthesisCallback(FileChannel fileChannel) {
565acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak        mFileChannel = fileChannel;
5750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
5850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
5950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
6050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    void stop() {
6150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
6250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            mStopped = true;
6350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            cleanUp();
6450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
6550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
6650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
6750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    /**
6850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * Must be called while holding the monitor on {@link #mStateLock}.
6950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     */
7050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    private void cleanUp() {
715acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak        closeFile();
7250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
7350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
7450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    /**
7550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     * Must be called while holding the monitor on {@link #mStateLock}.
7650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert     */
775acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak    private void closeFile() {
7850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        try {
795acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak            if (mFileChannel != null) {
805acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak                mFileChannel.close();
815acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak                mFileChannel = null;
8250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
8350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        } catch (IOException ex) {
845acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak            Log.e(TAG, "Failed to close output file descriptor", ex);
859c3d7a888d0c5c09f0153e81018ff68aa6e91712Narayan Kamath        }
869c3d7a888d0c5c09f0153e81018ff68aa6e91712Narayan Kamath    }
879c3d7a888d0c5c09f0153e81018ff68aa6e91712Narayan Kamath
8850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
8971e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    public int getMaxBufferSize() {
9071e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        return MAX_AUDIO_BUFFER_SIZE;
9171e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    }
9271e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert
9371e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    @Override
94360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    boolean isDone() {
95360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert        return mDone;
96360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    }
97360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert
98360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    @Override
9950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int start(int sampleRateInHz, int audioFormat, int channelCount) {
10050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) {
10150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            Log.d(TAG, "FileSynthesisRequest.start(" + sampleRateInHz + "," + audioFormat
10250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                    + "," + channelCount + ")");
10350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
10450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
10550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            if (mStopped) {
10650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                if (DBG) Log.d(TAG, "Request has been aborted.");
10750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
10850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1095acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak            if (mStarted) {
11050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                cleanUp();
11150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                throw new IllegalArgumentException("FileSynthesisRequest.start() called twice");
11250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1135acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak            mStarted = true;
11450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            mSampleRateInHz = sampleRateInHz;
11550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            mAudioFormat = audioFormat;
11650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            mChannelCount = channelCount;
1175acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak
11850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            try {
1195acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak                mFileChannel.write(ByteBuffer.allocate(WAV_HEADER_LENGTH));
12050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.SUCCESS;
12150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            } catch (IOException ex) {
1225acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak                Log.e(TAG, "Failed to write wav header to output file descriptor" + ex);
12350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                cleanUp();
12450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
12550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
12650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
12750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
12850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
12950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
13050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int audioAvailable(byte[] buffer, int offset, int length) {
13150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) {
13250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            Log.d(TAG, "FileSynthesisRequest.audioAvailable(" + buffer + "," + offset
13350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                    + "," + length + ")");
13450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
13550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
13650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            if (mStopped) {
13750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                if (DBG) Log.d(TAG, "Request has been aborted.");
13850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
13950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1405acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak            if (mFileChannel == null) {
14150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                Log.e(TAG, "File not open");
14250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
14350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
14450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            try {
1455acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak                mFileChannel.write(ByteBuffer.wrap(buffer,  offset,  length));
14650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.SUCCESS;
14750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            } catch (IOException ex) {
1485acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak                Log.e(TAG, "Failed to write to output file descriptor", ex);
14950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                cleanUp();
15050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
15150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
15250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
15350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
15450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
15550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    @Override
15650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    public int done() {
15750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        if (DBG) Log.d(TAG, "FileSynthesisRequest.done()");
15850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        synchronized (mStateLock) {
1599c3d7a888d0c5c09f0153e81018ff68aa6e91712Narayan Kamath            if (mDone) {
1609c3d7a888d0c5c09f0153e81018ff68aa6e91712Narayan Kamath                if (DBG) Log.d(TAG, "Duplicate call to done()");
1619c3d7a888d0c5c09f0153e81018ff68aa6e91712Narayan Kamath                // This preserves existing behaviour. Earlier, if done was called twice
1629c3d7a888d0c5c09f0153e81018ff68aa6e91712Narayan Kamath                // we'd return ERROR because mFile == null and we'd add to logspam.
1639c3d7a888d0c5c09f0153e81018ff68aa6e91712Narayan Kamath                return TextToSpeech.ERROR;
1649c3d7a888d0c5c09f0153e81018ff68aa6e91712Narayan Kamath            }
16550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            if (mStopped) {
16650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                if (DBG) Log.d(TAG, "Request has been aborted.");
16750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
16850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
1695acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak            if (mFileChannel == null) {
17050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                Log.e(TAG, "File not open");
17150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
17250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
17350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            try {
17450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                // Write WAV header at start of file
1755acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak                mFileChannel.position(0);
1765acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak                int dataLength = (int) (mFileChannel.size() - WAV_HEADER_LENGTH);
1775acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak                mFileChannel.write(
17871e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert                        makeWavHeader(mSampleRateInHz, mAudioFormat, mChannelCount, dataLength));
1795acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak                closeFile();
180360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert                mDone = true;
18150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.SUCCESS;
18250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            } catch (IOException ex) {
1835acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak                Log.e(TAG, "Failed to write to output file descriptor", ex);
18450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                cleanUp();
18550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert                return TextToSpeech.ERROR;
18650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert            }
18750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        }
18850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
18950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
19071e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert    @Override
191360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    public void error() {
192360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert        if (DBG) Log.d(TAG, "FileSynthesisRequest.error()");
193360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert        synchronized (mStateLock) {
194360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert            cleanUp();
195360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert        }
196360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert    }
197360eb168d6f9c967543852c7bbab370ef5d8c1bdBjorn Bringert
1985acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak    private ByteBuffer makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount,
19971e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert            int dataLength) {
20050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        // TODO: is AudioFormat.ENCODING_DEFAULT always the same as ENCODING_PCM_16BIT?
20150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        int sampleSizeInBytes = (audioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2);
20250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        int byteRate = sampleRateInHz * sampleSizeInBytes * channelCount;
20350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        short blockAlign = (short) (sampleSizeInBytes * channelCount);
20450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        short bitsPerSample = (short) (sampleSizeInBytes * 8);
20550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
20650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        byte[] headerBuf = new byte[WAV_HEADER_LENGTH];
20750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        ByteBuffer header = ByteBuffer.wrap(headerBuf);
20850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.order(ByteOrder.LITTLE_ENDIAN);
20950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
21050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.put(new byte[]{ 'R', 'I', 'F', 'F' });
21171e0b4807797c602e7fc787d00d27c4f9c92a507Bjorn Bringert        header.putInt(dataLength + WAV_HEADER_LENGTH - 8);  // RIFF chunk size
21250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.put(new byte[]{ 'W', 'A', 'V', 'E' });
21350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.put(new byte[]{ 'f', 'm', 't', ' ' });
21450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.putInt(16);  // size of fmt chunk
21550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.putShort(WAV_FORMAT_PCM);
21650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.putShort((short) channelCount);
21750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.putInt(sampleRateInHz);
21850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.putInt(byteRate);
21950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.putShort(blockAlign);
22050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.putShort(bitsPerSample);
22150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.put(new byte[]{ 'd', 'a', 't', 'a' });
22250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert        header.putInt(dataLength);
2235acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak        header.flip();
22450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
2255acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak        return header;
22650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert    }
22750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert
22850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert}
229