/* ***************************************************************************+
 * ITX package (cnrg.itx) for telephony application programming.              *
 * Copyright (c) 1999  Cornell University, Ithaca NY.                         *
 * A copy of the license is distributed with this package.  Look in the docs  *
 * directory, filename GPL.  Contact information: bergmark@cs.cornell.edu     *
 ******************************************************************************/

package cnrg.itx.datax.devices;

import java.io.*;
import cnrg.itx.datax.*;
import cnrg.itx.datax.jaudio.*;

/**
 * Class to play output to the computer speaker.  Uses the jaudio system to 
 * communicate with the sound device.  Consequently, jaudio.dll must be in your 
 * system path.
 */
public class SpeakerDestination implements Destination
{
	/**
	 * Sample size (MUST use the Channel sample size!)
	 */
	public static final int SAMPLE_SIZE = Channel.SAMPLE_SIZE;	
	
	/**
	 * JAudioBlock queue size
	 */
	private static final int QUEUE_SIZE	= 100;	
	
	private JAudioDevice jad                 = null;
	private JAudioBlock jabLastBlock         = null;
	private JAudioBlock jabBlockToWaitOn     = null;

	private JABQueue qUsed                   = null;
	private JABQueue qFree                   = null;

	private boolean bMute                    = false;
	private boolean bOpen					 = false;
	
	private Stats speakerStats;
	int iDevice = 0;
	private int bytesRead = 0;
	
	// write() variables
	private JAudioBlock jabTemp;
	private byte[] bData;
	private byte[] bOldData;
	private int iRead;	
	
	/**
	 * Creates a speaker object by acquiring the necessary resources from jaudio.
	 * @throws DataException if resources could not be acquired.
	 */
	public SpeakerDestination() throws DataException 
	{
		// Get a JAudioDevice
		while (jad == null && iDevice < JAudioDevice.getNumDevices()) 
		{
			try 
			{ 
				jad = new JAudioDevice(iDevice, JAudioDevice.OUTPUT);
				
			}
			catch (JAudioException jae) 
			{ 
				jae.printStackTrace();
			}
			iDevice++;
		}
		if (jad == null) throw new DataException("Couldn't find a free JAudioDevice");

		// create queues & fill with audioBlocks
		qFree = new JABQueue();
		qUsed = new JABQueue();

		for (int i = 0; i < QUEUE_SIZE; i++)
		{
			qFree.enqueue(new JAudioBlock(new byte[SAMPLE_SIZE]));
		}

		// Initialize the statistics object
		speakerStats = new Stats();
		
		// Start the JAudioDevice
		try
		{
			jad.out.start();
			bOpen = true;
		}
		catch (JAudioException jae)
		{
			throw new DataException("Error opening JAudioDevice");
		}
	}
	
	/**
	 * Method to write data to the speaker.
	 * @param b the byte array to be written to the speaker
	 * @exception DataException thrown if the size of the data buffer is not valid
	 */
	public void write(byte [] b) throws DataException 
	{
		// Check sample size
		if (b.length != SAMPLE_SIZE)
		{
			throw new DataException("Illegal sample size");
		}
		
		try
		{
			jabTemp = (JAudioBlock)qFree.dequeue();
			while (jabTemp == null)
			{
				jabTemp = (JAudioBlock)qUsed.dequeue();
				if (jabTemp != null)
					jabTemp.waitUntilFinished();
			}		
			bData = jabTemp.getData();

			System.arraycopy(b, 0, bData, 0, SAMPLE_SIZE);
			
			if (!bMute && bOpen)
			{
				jad.out.putBlock(jabTemp);
				qUsed.enqueue(jabTemp);
				jabLastBlock = jabTemp;
			}
			else
			{
				qFree.enqueue(jabTemp);
			}
		}
		catch(Exception e)
		{
			System.out.println("Exception: " + e);
			e.printStackTrace();
		}
		
		bytesRead += SAMPLE_SIZE;
		speakerStats.addStat("<JaudioDevice  " + iDevice + "> Bytes read: ", 
							 new Integer(bytesRead));
	}
	
	/**
	 * Closes the device.
	 */
	public void close()
	{ 	
		try
		{
			bOpen = false;

			jad.close();			
		}
		catch (JAudioException jae)
		{
			jae.printStackTrace();
		}
	}

	/**
	 * Mutes the device
	 * @param mute the device
	 */
	public boolean mute(boolean mute) 
	{ 
		bMute = mute;
		return true;
	}
	
	/**
	 * Method to get the statistics for the speaker device.
	 * @return Stats the statistics for the speaker device
	 */
	public Stats getStatistics () 
	{ 
		return speakerStats; 
	}
	
	/**
	 * Returns a collection of properties supported.
	 */ 
	public PropertiesCollection getProperties() throws DataException
	{
		return null;
	}

	/**
	 * Sets the given properties collection into the device
	 */
	public void setProperties(PropertiesCollection pc) throws DataException
	{
	}

	/**
	 * Interface to set the given properties collection into the device. WOrks under the 
	 * assumption that this is the properties collection of the peer.
	 */
	public void setPeerProperties(PropertiesCollection pc) throws DataException
	{	
	}
}
