/*
 *	TSoftClip.java
 *
 *	This file is part of Tritonus: http://www.tritonus.org/
 */

/*
 *  Copyright (c) 1999 by Matthias Pfisterer
 *
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as published
 *   by the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*
|<---            this code is formatted to fit into 80 columns             --->|
*/

package org.tritonus.share.sampled.mixer;

import java.io.IOException;
import java.io.ByteArrayOutputStream;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;

import org.tritonus.share.TDebug;



public class TSoftClip
extends TClip
implements Runnable
{
	//$$fb the following field is never used
	//private static final Class[]	CONTROL_CLASSES = {/*GainControl.class*/};
	private static final int	BUFFER_SIZE = 16384;


	//$$fb the following field is never used
	//private Mixer			m_mixer;
	private SourceDataLine		m_line;
	private byte[]			m_abClip;
	private int			m_nRepeatCount;
	private Thread			m_thread;

	public TSoftClip(Mixer mixer, AudioFormat format)
		throws LineUnavailableException
	{
		// TODO: info object
/*
		DataLine.Info	info = new DataLine.Info(Clip.class,
							 audioFormat, -1);
*/
		super(null);
		//m_mixer = mixer;
		DataLine.Info	info = new DataLine.Info(
			SourceDataLine.class,
			// TODO: should pass a real AudioFormat object that isn't too restrictive
			format);
		m_line = (SourceDataLine) AudioSystem.getLine(info);
	}



	public void open(AudioInputStream audioInputStream)
		throws LineUnavailableException, IOException
	{
		AudioFormat	audioFormat = audioInputStream.getFormat();
		setFormat(audioFormat);
		int	nFrameSize = audioFormat.getFrameSize();
		if (nFrameSize < 1)
		{
			throw new IllegalArgumentException("frame size must be positive");
		}
		if (TDebug.TraceClip)
		{
			TDebug.out("TSoftClip.open(): format: " + audioFormat);
			// TDebug.out("sample rate: " + audioFormat.getSampleRate());
		}
		byte[]	abData = new byte[BUFFER_SIZE];
		ByteArrayOutputStream	baos = new ByteArrayOutputStream();
		int	nBytesRead = 0;
		while (nBytesRead != -1)
		{
			try
			{
				nBytesRead = audioInputStream.read(abData, 0, abData.length);
			}
			catch (IOException e)
			{
				if (TDebug.TraceClip || TDebug.TraceAllExceptions)
				{
					TDebug.out(e);
				}
			}
			if (nBytesRead >= 0)
			{
				if (TDebug.TraceClip)
				{
					TDebug.out("TSoftClip.open(): Trying to write: " + nBytesRead);
				}
				baos.write(abData, 0, nBytesRead);
				if (TDebug.TraceClip)
				{
					TDebug.out("TSoftClip.open(): Written: " + nBytesRead);
				}
			}
		}
		m_abClip = baos.toByteArray();
		setBufferSize(m_abClip.length);
		// open the line
		m_line.open(getFormat());
		// to trigger the events
		// open();
	}



	public int getFrameLength()
	{
		if (isOpen())
		{
			return getBufferSize() / getFormat().getFrameSize();
		}
		else
		{
			return AudioSystem.NOT_SPECIFIED;
		}
	}



	public long getMicrosecondLength()
	{
		if (isOpen())
		{
			return (long) (getFrameLength() * getFormat().getFrameRate() * 1000000);
		}
		else
		{
			return AudioSystem.NOT_SPECIFIED;
		}
	}



	public void setFramePosition(int nPosition)
	{
		// TOOD:
	}



	public void setMicrosecondPosition(long lPosition)
	{
		// TOOD:
	}



	public int getFramePosition()
	{
		// TOOD:
		return -1;
	}



	public long getMicrosecondPosition()
	{
		// TOOD:
		return -1;
	}



	public void setLoopPoints(int nStart, int nEnd)
	{
		// TOOD:
	}



	public void loop(int nCount)
	{
		if (TDebug.TraceClip)
		{
			TDebug.out("TSoftClip.loop(int): called; count = " + nCount);
		}
		if (false/*isStarted()*/)
		{
			/*
			 *	only allow zero count to stop the looping
			 *	at the end of an iteration.
			 */
			if (nCount == 0)
			{
				if (TDebug.TraceClip)
				{
					TDebug.out("TSoftClip.loop(int): stopping sample");
				}
				// m_esdSample.stop();
			}
		}
		else
		{
			m_nRepeatCount = nCount;
			m_thread = new Thread(this);
			m_thread.start();
		}
		// TOOD:
	}



	public void flush()
	{
		// TOOD:
	}



	public void drain()
	{
		// TOOD:
	}



	public void close()
	{
		// m_esdSample.free();
		// m_esdSample.close();
		// TOOD:
	}




	public void open()
	{
		// TODO:
	}



	public void start()
	{
		if (TDebug.TraceClip)
		{
			TDebug.out("TSoftClip.start(): called");
		}
		/*
		 *	This is a hack. What start() really should do is
		 *	start playing at the position playback was stopped.
		 */
		if (TDebug.TraceClip)
		{
			TDebug.out("TSoftClip.start(): calling 'loop(0)' [hack]");
		}
		loop(0);
	}



	public void stop()
	{
		// TODO:
		// m_esdSample.kill();
	}



	/*
	 *	This method is enforced by DataLine, but doesn't make any
	 *	sense for Clips.
	 */
	public int available()
	{
		return -1;
	}



	public void run()
	{
		while (m_nRepeatCount >= 0)
		{
			m_line.write(m_abClip, 0, m_abClip.length);
			m_nRepeatCount--;
		}
	}

}



/*** TSoftClip.java ***/

