Home | History | Annotate | Download | only in speechrecorder
      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      */
     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      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    216     private static int readInt(InputStream in) throws IOException {
    217         return in.read() | (in.read() << 8) | (in.read() << 16) | (in.read() << 24);
    218     }
    219 
    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      */
    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 
    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 
    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 
    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
    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