WaveHeader.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.speech.srec; 18 19import java.io.IOException; 20import java.io.InputStream; 21import java.io.OutputStream; 22 23/** 24 * This class represents the header of a WAVE format audio file, which usually 25 * have a .wav suffix. The following integer valued fields are contained: 26 * <ul> 27 * <li> format - usually PCM, ALAW or ULAW. 28 * <li> numChannels - 1 for mono, 2 for stereo. 29 * <li> sampleRate - usually 8000, 11025, 16000, 22050, or 44100 hz. 30 * <li> bitsPerSample - usually 16 for PCM, 8 for ALAW, or 8 for ULAW. 31 * <li> numBytes - size of audio data after this header, in bytes. 32 * </ul> 33 * @hide pending API council approval 34 */ 35public class WaveHeader { 36 37 // follows WAVE format in http://ccrma.stanford.edu/courses/422/projects/WaveFormat 38 39 private static final String TAG = "WaveHeader"; 40 41 private static final int HEADER_LENGTH = 44; 42 43 /** Indicates PCM format. */ 44 public static final short FORMAT_PCM = 1; 45 /** Indicates ALAW format. */ 46 public static final short FORMAT_ALAW = 6; 47 /** Indicates ULAW format. */ 48 public static final short FORMAT_ULAW = 7; 49 50 private short mFormat; 51 private short mNumChannels; 52 private int mSampleRate; 53 private short mBitsPerSample; 54 private int mNumBytes; 55 56 /** 57 * Construct a WaveHeader, with all fields defaulting to zero. 58 */ 59 public WaveHeader() { 60 } 61 62 /** 63 * Construct a WaveHeader, with fields initialized. 64 * @param format format of audio data, 65 * one of {@link #FORMAT_PCM}, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}. 66 * @param numChannels 1 for mono, 2 for stereo. 67 * @param sampleRate typically 8000, 11025, 16000, 22050, or 44100 hz. 68 * @param bitsPerSample usually 16 for PCM, 8 for ULAW or 8 for ALAW. 69 * @param numBytes size of audio data after this header, in bytes. 70 */ 71 public WaveHeader(short format, short numChannels, int sampleRate, short bitsPerSample, int numBytes) { 72 mFormat = format; 73 mSampleRate = sampleRate; 74 mNumChannels = numChannels; 75 mBitsPerSample = bitsPerSample; 76 mNumBytes = numBytes; 77 } 78 79 /** 80 * Get the format field. 81 * @return format field, 82 * one of {@link #FORMAT_PCM}, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}. 83 */ 84 public short getFormat() { 85 return mFormat; 86 } 87 88 /** 89 * Set the format field. 90 * @param format 91 * one of {@link #FORMAT_PCM}, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}. 92 * @return reference to this WaveHeader instance. 93 */ 94 public WaveHeader setFormat(short format) { 95 mFormat = format; 96 return this; 97 } 98 99 /** 100 * Get the number of channels. 101 * @return number of channels, 1 for mono, 2 for stereo. 102 */ 103 public short getNumChannels() { 104 return mNumChannels; 105 } 106 107 /** 108 * Set the number of channels. 109 * @param numChannels 1 for mono, 2 for stereo. 110 * @return reference to this WaveHeader instance. 111 */ 112 public WaveHeader setNumChannels(short numChannels) { 113 mNumChannels = numChannels; 114 return this; 115 } 116 117 /** 118 * Get the sample rate. 119 * @return sample rate, typically 8000, 11025, 16000, 22050, or 44100 hz. 120 */ 121 public int getSampleRate() { 122 return mSampleRate; 123 } 124 125 /** 126 * Set the sample rate. 127 * @param sampleRate sample rate, typically 8000, 11025, 16000, 22050, or 44100 hz. 128 * @return reference to this WaveHeader instance. 129 */ 130 public WaveHeader setSampleRate(int sampleRate) { 131 mSampleRate = sampleRate; 132 return this; 133 } 134 135 /** 136 * Get the number of bits per sample. 137 * @return number of bits per sample, 138 * usually 16 for PCM, 8 for ULAW or 8 for ALAW. 139 */ 140 public short getBitsPerSample() { 141 return mBitsPerSample; 142 } 143 144 /** 145 * Set the number of bits per sample. 146 * @param bitsPerSample number of bits per sample, 147 * usually 16 for PCM, 8 for ULAW or 8 for ALAW. 148 * @return reference to this WaveHeader instance. 149 */ 150 public WaveHeader setBitsPerSample(short bitsPerSample) { 151 mBitsPerSample = bitsPerSample; 152 return this; 153 } 154 155 /** 156 * Get the size of audio data after this header, in bytes. 157 * @return size of audio data after this header, in bytes. 158 */ 159 public int getNumBytes() { 160 return mNumBytes; 161 } 162 163 /** 164 * Set the size of audio data after this header, in bytes. 165 * @param numBytes size of audio data after this header, in bytes. 166 * @return reference to this WaveHeader instance. 167 */ 168 public WaveHeader setNumBytes(int numBytes) { 169 mNumBytes = numBytes; 170 return this; 171 } 172 173 /** 174 * Read and initialize a WaveHeader. 175 * @param in {@link java.io.InputStream} to read from. 176 * @return number of bytes consumed. 177 * @throws IOException 178 */ 179 public int read(InputStream in) throws IOException { 180 /* RIFF header */ 181 readId(in, "RIFF"); 182 int numBytes = readInt(in) - 36; 183 readId(in, "WAVE"); 184 185 /* fmt chunk */ 186 readId(in, "fmt "); 187 if (16 != readInt(in)) throw new IOException("fmt chunk length not 16"); 188 mFormat = readShort(in); 189 mNumChannels = readShort(in); 190 mSampleRate = readInt(in); 191 int byteRate = readInt(in); 192 short blockAlign = readShort(in); 193 mBitsPerSample = readShort(in); 194 if (byteRate != mNumChannels * mSampleRate * mBitsPerSample / 8) { 195 throw new IOException("fmt.ByteRate field inconsistent"); 196 } 197 if (blockAlign != mNumChannels * mBitsPerSample / 8) { 198 throw new IOException("fmt.BlockAlign field inconsistent"); 199 } 200 201 /* data chunk */ 202 readId(in, "data"); 203 mNumBytes = readInt(in); 204 205 return HEADER_LENGTH; 206 } 207 208 private static void readId(InputStream in, String id) throws IOException { 209 for (int i = 0; i < id.length(); i++) { 210 if (id.charAt(i) != in.read()) throw new IOException( id + " tag not present"); 211 } 212 } 213 214 private static int readInt(InputStream in) throws IOException { 215 return in.read() | (in.read() << 8) | (in.read() << 16) | (in.read() << 24); 216 } 217 218 private static short readShort(InputStream in) throws IOException { 219 return (short)(in.read() | (in.read() << 8)); 220 } 221 222 /** 223 * Write a WAVE file header. 224 * @param out {@link java.io.OutputStream} to receive the header. 225 * @return number of bytes written. 226 * @throws IOException 227 */ 228 public int write(OutputStream out) throws IOException { 229 /* RIFF header */ 230 writeId(out, "RIFF"); 231 writeInt(out, 36 + mNumBytes); 232 writeId(out, "WAVE"); 233 234 /* fmt chunk */ 235 writeId(out, "fmt "); 236 writeInt(out, 16); 237 writeShort(out, mFormat); 238 writeShort(out, mNumChannels); 239 writeInt(out, mSampleRate); 240 writeInt(out, mNumChannels * mSampleRate * mBitsPerSample / 8); 241 writeShort(out, (short)(mNumChannels * mBitsPerSample / 8)); 242 writeShort(out, mBitsPerSample); 243 244 /* data chunk */ 245 writeId(out, "data"); 246 writeInt(out, mNumBytes); 247 248 return HEADER_LENGTH; 249 } 250 251 private static void writeId(OutputStream out, String id) throws IOException { 252 for (int i = 0; i < id.length(); i++) out.write(id.charAt(i)); 253 } 254 255 private static void writeInt(OutputStream out, int val) throws IOException { 256 out.write(val >> 0); 257 out.write(val >> 8); 258 out.write(val >> 16); 259 out.write(val >> 24); 260 } 261 262 private static void writeShort(OutputStream out, short val) throws IOException { 263 out.write(val >> 0); 264 out.write(val >> 8); 265 } 266 267 @Override 268 public String toString() { 269 return String.format( 270 "WaveHeader format=%d numChannels=%d sampleRate=%d bitsPerSample=%d numBytes=%d", 271 mFormat, mNumChannels, mSampleRate, mBitsPerSample, mNumBytes); 272 } 273 274} 275