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