/* ***************************************************************************+
 * 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 cnrg.itx.datax.jaudio.*;
import cnrg.itx.datax.*;

import java.io.*;

/**
 *  This class encapsulates the workings of the Microphone.  It uses the jaudio subsystem
 *  to talk to hardware.  Therefore, jaudio.dll must be in your path.
 *  .... Talk about usage..
 */
public class MicrophoneSource implements Runnable, Source
{
	/**
	 * Audio properties (8kHz @ 8 bits / sample)
	 */
	public static final int SAMPLE_RATE	= 8000;
	public static final int SAMPLE_BITS	= 8;
	public static final byte SILENCE	= 127;
	
	/**
	 * Sample size (just use the Channel sample size, although this isn't required)
	 */
	public static final int SAMPLE_SIZE = Channel.SAMPLE_SIZE;		
	
	/**
	 * JAudioBlock queue size
	 */
	private static final int QUEUE_SIZE	= 100;
	
	private JAudioDevice jad;
	private JAudioBlock [] jabBlocks;
	private boolean bAlive;
	private boolean bMute = false;
	private byte [] bSilence;
	private Thread thread;

	/**
	 * Attribute for storing the number of bytes written by this source.
	 */
	private int bytesWritten = 0;
	
	private Channel c;
	private int iDevice = 0;
	private Stats microphoneStats;
	
	/**
	 * Creates the microphone.  Allocates all resources.  
	 * @exception DataException thrown when resources could not be allocated.
	 */
	public MicrophoneSource(Channel c) throws DataException 
	{
		this.c = c;
		
		while (jad == null && iDevice < JAudioDevice.getNumDevices())
		{
			try 
			{
				jad = new JAudioDevice(iDevice, JAudioDevice.INPUT);
			}
			catch (JAudioException jae) { 
				jae.printStackTrace();
			}
				iDevice++;
		}

		if (jad == null) 
			throw new DataException("MicrophoneSource: Couldn't acquire microphone.");
		
		jabBlocks = new JAudioBlock[QUEUE_SIZE];
		
		for (int i = 0; i < QUEUE_SIZE; i++)
		{
			jabBlocks[i] = new JAudioBlock(SAMPLE_SIZE);
		}	  

		bSilence = new byte[SAMPLE_SIZE];
		
		for (int i = 0; i < SAMPLE_SIZE; i++)
		{
			bSilence[i] = SILENCE;
		}
		
		// Initialize the statistics for the microphone
		microphoneStats = new Stats();
	}

	/**
	 * Method to start the thread of the source
	 */
	public void start()
	{
		thread = new Thread(this);
		thread.start();
		
		// Reset stream offset
		bytesWritten = 0;
	}
	
	/**
	 * Method to stop the thread.
	 */
	public void stop()
	{
		if (bAlive == false)
		{
			return;
		}
		
		bAlive = false;
		
		if (thread != null && thread.isAlive())
		{
			try
			{
				thread.join();
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
		}
		
		thread = null;
		
		// Reset stream offset
		bytesWritten = 0;
	}

	/** 
	 * Method to run the thread.
	 */
	public void run()
	{
		int iCurrent = 0;
		
		try 
		{
			bAlive = true;
			
			for (int i = 0; i < QUEUE_SIZE; i++)
			{
				jad.in.addBlock(jabBlocks[i]);
			} 
			
			jad.in.start();
			
			while (bAlive)
			{
				jabBlocks[iCurrent].waitUntilFinished();
				
				if (! bMute)
				{
					c.push(jabBlocks[iCurrent].getData());
				}
				
				// Increment the stream offset and add stats
				bytesWritten += SAMPLE_SIZE;
				microphoneStats.addStat("<JAudioDevice  " + iDevice + "> Bytes Written: ",
										new Integer(bytesWritten));
				
				jad.in.addBlock(jabBlocks[iCurrent]);
				
				iCurrent = (iCurrent + 1) % QUEUE_SIZE;
			}
		}
		catch (JAudioException jae )
		{
			jae.printStackTrace();
		}
		catch (InterruptedException ie)
		{
			ie.printStackTrace();
		}
	}

	/**
	 * stops playout.
	 */
	public void close()  
	{ 
		try
		{
			stop();
			jad.close();
		}
		catch( JAudioException jae)
		{
			jae.printStackTrace();
		}
		
	}
	
	/**
	 * Mutes the playout.
	 * @param mute The new state.
	 *
	 */
	public boolean mute(boolean mute) 
	{ 
		bMute = mute;
		return true;
	}
	
	/**
	 * Method to get the statistics of the microphone device.
	 */
	public Stats getStatistics () 
	{ 
		return microphoneStats;
	}
	
	/**
	 * Returns a collection of properties supported.
	 */ 
	public PropertiesCollection getProperties() throws DataException
	{
		return null;
	}

	/**
	 * Sets the given properties collection into the device (local);
	 */
	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
	{
	}
}
