package kr.util.audio;


/**
 * Yea, I'm not using java.sound or jmf, but both are rather complicated and not really needed for this simple
 * program. So this is a more lightweight interface. This is a simple audio clip.
 * <p>
 * Data is in PCM LINEAR 8 or 16 bit little endian signed format. If multi channel, they are interleaved.
 * @see WritableAudioClip For a AudioClip that can be written to, 
 */
public abstract class AudioClip
{
	private String name;
	
	public AudioClip()
	{
	}
	
	public AudioClip(String name)
	{
		this.name = name;
	}

	public abstract int getNumChannels();
	
	/**
	 * Returns length in bytes
	 */
	public abstract int length();
	
	public abstract int getRate();
	
	public int getSampleSizeInBits()
	{
		return 8; //default 8
	}
	
	public abstract byte byteAt(int index);
	
	/**
	 * Copies data to the byte array
	 * @param offset offset in bytes
	 * @param length length in bytes
	 * @param destOffset destination offset in bytes
	 */
	public abstract void copyTo(int offset, int length, byte [] dest, int destOffset);

	public int getIndexFromMicros(long micros) {
		return (int)Math.round(micros / getMicrosToIndexRatio());
	}

	public long getMicrosFromIndex(int index) {
		return (int)Math.round(index * getMicrosToIndexRatio());
	}

	public double getMicrosToIndexRatio() {
		return (double)1000000./ getRate() / getNumChannels() * 8 / getSampleSizeInBits() ;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getBytesPerSecond() {
		return getRate() * getNumChannels() * getSampleSizeInBits() / 8;
	}

	public int getMaxMicros() {
		return (int)Math.round(getMicrosFromIndex(length()));
	}

	public double getMicrosToFrameRatio() {
		return (double)1000000./ getRate();
	}

	public int getFrameFromMicros(long micros) {
		return (int)Math.round(micros / getMicrosToFrameRatio());
	}

	public int sizeInFrames() {
		if(getSampleSizeInBits() == 0 || getNumChannels()==0)
			return 0;
		return length() * (8 / getSampleSizeInBits()) / getNumChannels();
	}

	/**
	 * Returns a value from 0 to 1
	 */
	public float frameFloatVal(int frame, int channel) {
		float maxVal = (1 << (getSampleSizeInBits()-1)) - 1;

		return (((float)frameVal(frame, channel)) + maxVal) / (maxVal * 2); 
	}
	
	public int frameVal(int frame, int channel) {
		int index = frame * getSampleSizeInBits() / 8 * getNumChannels();
		if(getSampleSizeInBits() == 16)
		{
			byte b1 = byteAt(index);
			byte b2 = byteAt(index+1);
			return (int)(b1 & 0xff) + (b2 << 8);
		}
		return byteAt(index);
	}
}
