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