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 17 package com.android.speechrecorder; 18 19 import java.io.IOException; 20 import java.io.InputStream; 21 import 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 * 34 * Not yet ready to be supported, so 35 * @hide 36 */ 37 public class WaveHeader { 38 39 // follows WAVE format in http://ccrma.stanford.edu/courses/422/projects/WaveFormat 40 41 private static final String TAG = "WaveHeader"; 42 43 private static final int HEADER_LENGTH = 44; 44 45 /** Indicates PCM format. */ 46 public static final short FORMAT_PCM = 1; 47 /** Indicates ALAW format. */ 48 public static final short FORMAT_ALAW = 6; 49 /** Indicates ULAW format. */ 50 public static final short FORMAT_ULAW = 7; 51 52 private short mFormat; 53 private short mNumChannels; 54 private int mSampleRate; 55 private short mBitsPerSample; 56 private int mNumBytes; 57 58 /** 59 * Construct a WaveHeader, with all fields defaulting to zero. 60 */ WaveHeader()61 public WaveHeader() { 62 } 63 64 /** 65 * Construct a WaveHeader, with fields initialized. 66 * @param format format of audio data, 67 * one of {@link #FORMAT_PCM}, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}. 68 * @param numChannels 1 for mono, 2 for stereo. 69 * @param sampleRate typically 8000, 11025, 16000, 22050, or 44100 hz. 70 * @param bitsPerSample usually 16 for PCM, 8 for ULAW or 8 for ALAW. 71 * @param numBytes size of audio data after this header, in bytes. 72 */ WaveHeader(short format, short numChannels, int sampleRate, short bitsPerSample, int numBytes)73 public WaveHeader(short format, short numChannels, int sampleRate, short bitsPerSample, int numBytes) { 74 mFormat = format; 75 mSampleRate = sampleRate; 76 mNumChannels = numChannels; 77 mBitsPerSample = bitsPerSample; 78 mNumBytes = numBytes; 79 } 80 81 /** 82 * Get the format field. 83 * @return format field, 84 * one of {@link #FORMAT_PCM}, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}. 85 */ getFormat()86 public short getFormat() { 87 return mFormat; 88 } 89 90 /** 91 * Set the format field. 92 * @param format 93 * one of {@link #FORMAT_PCM}, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}. 94 * @return reference to this WaveHeader instance. 95 */ setFormat(short format)96 public WaveHeader setFormat(short format) { 97 mFormat = format; 98 return this; 99 } 100 101 /** 102 * Get the number of channels. 103 * @return number of channels, 1 for mono, 2 for stereo. 104 */ getNumChannels()105 public short getNumChannels() { 106 return mNumChannels; 107 } 108 109 /** 110 * Set the number of channels. 111 * @param numChannels 1 for mono, 2 for stereo. 112 * @return reference to this WaveHeader instance. 113 */ setNumChannels(short numChannels)114 public WaveHeader setNumChannels(short numChannels) { 115 mNumChannels = numChannels; 116 return this; 117 } 118 119 /** 120 * Get the sample rate. 121 * @return sample rate, typically 8000, 11025, 16000, 22050, or 44100 hz. 122 */ getSampleRate()123 public int getSampleRate() { 124 return mSampleRate; 125 } 126 127 /** 128 * Set the sample rate. 129 * @param sampleRate sample rate, typically 8000, 11025, 16000, 22050, or 44100 hz. 130 * @return reference to this WaveHeader instance. 131 */ setSampleRate(int sampleRate)132 public WaveHeader setSampleRate(int sampleRate) { 133 mSampleRate = sampleRate; 134 return this; 135 } 136 137 /** 138 * Get the number of bits per sample. 139 * @return number of bits per sample, 140 * usually 16 for PCM, 8 for ULAW or 8 for ALAW. 141 */ getBitsPerSample()142 public short getBitsPerSample() { 143 return mBitsPerSample; 144 } 145 146 /** 147 * Set the number of bits per sample. 148 * @param bitsPerSample number of bits per sample, 149 * usually 16 for PCM, 8 for ULAW or 8 for ALAW. 150 * @return reference to this WaveHeader instance. 151 */ setBitsPerSample(short bitsPerSample)152 public WaveHeader setBitsPerSample(short bitsPerSample) { 153 mBitsPerSample = bitsPerSample; 154 return this; 155 } 156 157 /** 158 * Get the size of audio data after this header, in bytes. 159 * @return size of audio data after this header, in bytes. 160 */ getNumBytes()161 public int getNumBytes() { 162 return mNumBytes; 163 } 164 165 /** 166 * Set the size of audio data after this header, in bytes. 167 * @param numBytes size of audio data after this header, in bytes. 168 * @return reference to this WaveHeader instance. 169 */ setNumBytes(int numBytes)170 public WaveHeader setNumBytes(int numBytes) { 171 mNumBytes = numBytes; 172 return this; 173 } 174 175 /** 176 * Read and initialize a WaveHeader. 177 * @param in {@link java.io.InputStream} to read from. 178 * @return number of bytes consumed. 179 * @throws IOException 180 */ read(InputStream in)181 public int read(InputStream in) throws IOException { 182 /* RIFF header */ 183 readId(in, "RIFF"); 184 int numBytes = readInt(in) - 36; 185 readId(in, "WAVE"); 186 187 /* fmt chunk */ 188 readId(in, "fmt "); 189 if (16 != readInt(in)) throw new IOException("fmt chunk length not 16"); 190 mFormat = readShort(in); 191 mNumChannels = readShort(in); 192 mSampleRate = readInt(in); 193 int byteRate = readInt(in); 194 short blockAlign = readShort(in); 195 mBitsPerSample = readShort(in); 196 if (byteRate != mNumChannels * mSampleRate * mBitsPerSample / 8) { 197 throw new IOException("fmt.ByteRate field inconsistent"); 198 } 199 if (blockAlign != mNumChannels * mBitsPerSample / 8) { 200 throw new IOException("fmt.BlockAlign field inconsistent"); 201 } 202 203 /* data chunk */ 204 readId(in, "data"); 205 mNumBytes = readInt(in); 206 207 return HEADER_LENGTH; 208 } 209 readId(InputStream in, String id)210 private static void readId(InputStream in, String id) throws IOException { 211 for (int i = 0; i < id.length(); i++) { 212 if (id.charAt(i) != in.read()) throw new IOException( id + " tag not present"); 213 } 214 } 215 readInt(InputStream in)216 private static int readInt(InputStream in) throws IOException { 217 return in.read() | (in.read() << 8) | (in.read() << 16) | (in.read() << 24); 218 } 219 readShort(InputStream in)220 private static short readShort(InputStream in) throws IOException { 221 return (short)(in.read() | (in.read() << 8)); 222 } 223 224 /** 225 * Write a WAVE file header. 226 * @param out {@link java.io.OutputStream} to receive the header. 227 * @return number of bytes written. 228 * @throws IOException 229 */ write(OutputStream out)230 public int write(OutputStream out) throws IOException { 231 /* RIFF header */ 232 writeId(out, "RIFF"); 233 writeInt(out, 36 + mNumBytes); 234 writeId(out, "WAVE"); 235 236 /* fmt chunk */ 237 writeId(out, "fmt "); 238 writeInt(out, 16); 239 writeShort(out, mFormat); 240 writeShort(out, mNumChannels); 241 writeInt(out, mSampleRate); 242 writeInt(out, mNumChannels * mSampleRate * mBitsPerSample / 8); 243 writeShort(out, (short)(mNumChannels * mBitsPerSample / 8)); 244 writeShort(out, mBitsPerSample); 245 246 /* data chunk */ 247 writeId(out, "data"); 248 writeInt(out, mNumBytes); 249 250 return HEADER_LENGTH; 251 } 252 writeId(OutputStream out, String id)253 private static void writeId(OutputStream out, String id) throws IOException { 254 for (int i = 0; i < id.length(); i++) out.write(id.charAt(i)); 255 } 256 writeInt(OutputStream out, int val)257 private static void writeInt(OutputStream out, int val) throws IOException { 258 out.write(val >> 0); 259 out.write(val >> 8); 260 out.write(val >> 16); 261 out.write(val >> 24); 262 } 263 writeShort(OutputStream out, short val)264 private static void writeShort(OutputStream out, short val) throws IOException { 265 out.write(val >> 0); 266 out.write(val >> 8); 267 } 268 269 @Override toString()270 public String toString() { 271 return String.format( 272 "WaveHeader format=%d numChannels=%d sampleRate=%d bitsPerSample=%d numBytes=%d", 273 mFormat, mNumChannels, mSampleRate, mBitsPerSample, mNumBytes); 274 } 275 276 } 277