// https://github.com/s4l4x/audio-analysis/blob/master/src/com/badlogic/audio/io/WaveDecoder.java

/**
 * A simple class that can read in the PCM data from a
 * Wav file, converting the data to signed 32-bit floats
 * in the range [-1,1], merging stereo channels to a mono
 * channel for processing. This only supports 16-bit signed
 * stereo and mono Wav files with a sampling rate of 44100.
 * 
 * @author mzechner
 *
 */
sclass WAVDecoder /*implements com.badlogic.audio.io.Decoder*/ implements AutoCloseable {
	/** the input stream we read from **/
	EndianDataInputStream in;
	
	/** number of channels **/
	int channels;
	
	/** sample rate in Herz**/
	float sampleRate;
	
	bool ignoreSampleRate = true;
	
	bool twentyFourBit;
	
	*(InputStream stream) ctex {
		in = new EndianDataInputStream( new BufferedInputStream( stream, 1024*1024) );		
		if( !in.read4ByteString().equals( "RIFF" ) )
			throw new IllegalArgumentException( "not a wav" );
		
		in.readIntLittleEndian();
		
		if( !in.read4ByteString().equals( "WAVE" ) )
			throw new IllegalArgumentException( "expected WAVE tag" );
		
		if( !in.read4ByteString().equals( "fmt " ) )
			throw new IllegalArgumentException( "expected fmt tag" );
		
		if( in.readIntLittleEndian() != 16 )
			throw new IllegalArgumentException( "expected wave chunk size to be 16" );
		
		int format;
		if ((format = in.readShortLittleEndian()) != 1)
			throw new IllegalArgumentException( "expected format to be 1, got: " + format);
		
		channels = in.readShortLittleEndian();
		//print("Channels: " + channels);
		sampleRate = in.readIntLittleEndian();
		if (sampleRate != 44100)
		  if (ignoreSampleRate)
		    print("Sample rate: " + sampleRate);
		  else
			  fail("Not 44100 sampling rate: " + sampleRate);
		in.readIntLittleEndian();
		in.readShortLittleEndian();
		int fmt = in.readShortLittleEndian();

    if (fmt == 24) twentyFourBit = true;
		else if (fmt != 16)
			fail("Only 16/24-bit samples supported: " + fmt);
		
		S tag;
		while (!(tag = in.read4ByteString()).equals("data")) {
		  print("Skipping tag " + tag);
			//fail( "expected data tag, got: " + tag);
			int len = in.readIntLittleEndian();
			for i to len: in.read();
		}
				
		in.readIntLittleEndian();
	}
	
	/**
	 * Tries to read in samples.length samples, merging stereo to a mono
	 * channel by averaging and converting non float formats to float 32-bit.
	 * Returns the number of samples actually read. Guarantees that samples.length
	 * samples are read in if there was enough data in the stream.
	 * 
	 * @param samples The samples array to write the samples to
	 * @return The number of samples actually read.
	 */
	public int readMonoSamples(short[] samples) {
		int readSamples = 0;
		for (int i = 0; i < samples.length; i++) {
			double sample = 0; 
			try {
				for j to channels:
					sample += readSample();
				sample /= channels;
				samples[i] = (short) iround(max(-32768, min(32767, sample)));
				readSamples++;
			}
			catch (Exception ex) { break; } // ouch
		}
		
		ret readSamples; 
	}
	
	public int readStereoSamples(short[] samples) {
		int readSamples = 0;
		for (int i = 0; i < samples.length; i += 2) {
			double sample = 0; 
			try {
			  short left = readSample();
			  short right;
			  if (channels > 1)
			    right = readSample();
			  else
			    right = left;
				samples[i] = left;
				samples[i+1] = right;
				readSamples += 2;
			}
			catch (Exception ex) { break; } // ouch
		}
		
		ret readSamples; 
	}
	
	short readSample() ctex {
	  if (twentyFourBit) in.read();
	  ret in.readShortLittleEndian();
	}
	
	public void close() ctex { in.close(); }
}