package be.tarsos.dsp.writer;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 
* this source code is copied from : https://android.googlesource.com/platform/frameworks/base.git/+/android-4.3_r2/core/java/android/speech/srec/WaveHeader.java
 
*/
/**
 
* This class represents the header of a WAVE format audio file, which usually
 
* have a .wav suffix.
  
The following integer valued fields are contained:
 
* <ul>
 
* <li> format - usually PCM, ALAW or ULAW.
 
* <li> numChannels - 1 for mono, 2 for stereo.
 
* <li> sampleRate - usually 8000, 11025, 16000, 22050, or 44100 hz.
 
* <li> bitsPerSample - usually 16 for PCM, 8 for ALAW, or 8 for ULAW.
 
* <li> numBytes - size of audio data after this header, in bytes.
 
* </ul>
 
*
 
* Not yet ready to be supported, so
 
* @hide
 
*/

public class WaveHeader {

    
// follows WAVE format in
 
http://ccrma.stanford.edu/courses/422/projects/WaveFormat

    
private static final int HEADER_LENGTH = 44;

    
/** Indicates PCM format. */
    
public static final short FORMAT_PCM = 1;
    
/** Indicates ALAW format. */
    
public static final short FORMAT_ALAW = 6;
    
/** Indicates ULAW format. */
    
public static final short FORMAT_ULAW = 7;

    
private short mFormat;
    
private short mNumChannels;
    
private int mSampleRate;
    
private short mBitsPerSample;
    
private int mNumBytes;

    
/**
     
* Construct a WaveHeader, with all fields defaulting to zero.
     
*/

    
public WaveHeader() {
    
}

    
/**
     
* Construct a WaveHeader, with fields initialized.
     
* @param format format of audio data,
     
* one of
 
, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}.
     
* @param numChannels 1 for mono, 2 for stereo.
     
* @param sampleRate typically 8000, 11025, 16000, 22050, or 44100 hz.
     
* @param bitsPerSample usually 16 for PCM, 8 for ULAW or 8 for ALAW.
     
* @param numBytes size of audio data after this header, in bytes.
     
*/

    
public WaveHeader(short format, short numChannels, int sampleRate, short bitsPerSample, int numBytes) {
        
mFormat = format;
        
mSampleRate = sampleRate;
        
mNumChannels = numChannels;
        
mBitsPerSample = bitsPerSample;
        
mNumBytes = numBytes;
    
}

    
/**
     
* Get the format field.
     
* @return format field,
     
* one of
 
, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}.
     
*/

    
public short getFormat() {
        
return mFormat;
    
}

    
/**
     
* Set the format field.
     
* @param format
     
* one of
 
, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}.
     
* @return reference to this WaveHeader instance.
     
*/

    
public WaveHeader setFormat(short format) {
        
mFormat = format;
        
return this;
    
}

    
/**
     
* Get the number of channels.
     
* @return number of channels, 1 for mono, 2 for stereo.
     
*/

    
public short getNumChannels() {
        
return mNumChannels;
    
}

    
/**
     
* Set the number of channels.
     
* @param numChannels 1 for mono, 2 for stereo.
     
* @return reference to this WaveHeader instance.
     
*/

    
public WaveHeader setNumChannels(short numChannels) {
        
mNumChannels = numChannels;
        
return this;
    
}

    
/**
     
* Get the sample rate.
     
* @return sample rate, typically 8000, 11025, 16000, 22050, or 44100 hz.
     
*/

    
public int getSampleRate() {
        
return mSampleRate;
    
}

    
/**
     
* Set the sample rate.
     
* @param sampleRate sample rate, typically 8000, 11025, 16000, 22050, or 44100 hz.
     
* @return reference to this WaveHeader instance.
     
*/

    
public WaveHeader setSampleRate(int sampleRate) {
        
mSampleRate = sampleRate;
        
return this;
    
}

    
/**
     
* Get the number of bits per sample.
     
* @return number of bits per sample,
     
* usually 16 for PCM, 8 for ULAW or 8 for ALAW.
     
*/

    
public short getBitsPerSample() {
        
return mBitsPerSample;
    
}

    
/**
     
* Set the number of bits per sample.
     
* @param bitsPerSample number of bits per sample,
     
* usually 16 for PCM, 8 for ULAW or 8 for ALAW.
     
* @return reference to this WaveHeader instance.
     
*/

    
public WaveHeader setBitsPerSample(short bitsPerSample) {
        
mBitsPerSample = bitsPerSample;
        
return this;
    
}

    
/**
     
* Get the size of audio data after this header, in bytes.
     
* @return size of audio data after this header, in bytes.
     
*/

    
public int getNumBytes() {
        
return mNumBytes;
    
}

    
/**
     
* Set the size of audio data after this header, in bytes.
     
* @param numBytes size of audio data after this header, in bytes.
     
* @return reference to this WaveHeader instance.
     
*/

    
public WaveHeader setNumBytes(int numBytes) {
        
mNumBytes = numBytes;
        
return this;
    
}

    
/**
     
* Read and initialize a WaveHeader.
     
* @param in
 
 
to read from.
     
* @return number of bytes consumed.
     
* @throws IOException
     
*/

    
public int read(InputStream in) throws IOException {
        
/* RIFF header */
        
readId(in, "RIFF");
        

        
readId(in, "WAVE");

        
/* fmt chunk */
        
readId(in, "fmt ");
        
if (16 != readInt(in)) throw new IOException("fmt chunk length not 16");
        
mFormat = readShort(in);
        
mNumChannels = readShort(in);
        
mSampleRate = readInt(in);
        
int byteRate = readInt(in);
        
short blockAlign = readShort(in);
        
mBitsPerSample = readShort(in);
        
if (byteRate != mNumChannels * mSampleRate * mBitsPerSample / 8) {
            
throw new IOException("fmt.ByteRate field inconsistent");
        
}
        
if (blockAlign != mNumChannels * mBitsPerSample / 8) {
            
throw new IOException("fmt.BlockAlign field inconsistent");
        
}

        
/* data chunk */
        
readId(in, "data");
        
mNumBytes = readInt(in);

        
return HEADER_LENGTH;
    
}

    
private static void readId(InputStream in, String id) throws IOException {
        
for (int i = 0; i < id.length(); i++) {
            
if (id.charAt(i) != in.read()) throw new IOException( id + " tag not present");
        
}
    
}

    
private static int readInt(InputStream in) throws IOException {
        
return in.read() | (in.read() << 8) | (in.read() << 16) | (in.read() << 24);
    
}

    
private static short readShort(InputStream in) throws IOException {
        
return (short)(in.read() | (in.read() << 8));
    
}

    
/**
     
* Write a WAVE file header.
     
* @param out
 
 
to receive the header.
     
* @return number of bytes written.
     
* @throws IOException
     
*/

    
public int write(OutputStream out) throws IOException {
        
/* RIFF header */
        
writeId(out, "RIFF");
        
writeInt(out, 36 + mNumBytes);
        
writeId(out, "WAVE");

        
/* fmt chunk */
        
writeId(out, "fmt ");
        
writeInt(out, 16);
        
writeShort(out, mFormat);
        
writeShort(out, mNumChannels);
        
writeInt(out, mSampleRate);
        
writeInt(out, mNumChannels * mSampleRate * mBitsPerSample / 8);
        
writeShort(out, (short)(mNumChannels * mBitsPerSample / 8));
        
writeShort(out, mBitsPerSample);

        
/* data chunk */
        
writeId(out, "data");
        
writeInt(out, mNumBytes);

        
return HEADER_LENGTH;
    
}

    
private static void writeId(OutputStream out, String id) throws IOException {
        
for (int i = 0; i < id.length(); i++) out.write(id.charAt(i));
    
}

    
private static void writeInt(OutputStream out, int val) throws IOException {
        
out.write(val >> 0);
        
out.write(val >> 8);
        
out.write(val >> 16);
        
out.write(val >> 24);
    
}

    
private static void writeShort(OutputStream out, short val) throws IOException {
        
out.write(val >> 0);
        
out.write(val >> 8);
    
}

    
@Override
    
public String toString() {
        
return String.format(
                
"WaveHeader format=%d numChannels=%d sampleRate=%d bitsPerSample=%d numBytes=%d",
                
mFormat, mNumChannels, mSampleRate, mBitsPerSample, mNumBytes);
    
}

}