/*
 *	TDirectSynthesizerTestCase.java
 */

/*
 *  Copyright (c) 2003 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.
 */

package org.tritonus.test.tritonus.share.midi;

import junit.framework.TestCase;

import javax.sound.midi.Synthesizer;
import javax.sound.midi.Instrument;
import javax.sound.midi.Patch;
import javax.sound.midi.Soundbank;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.VoiceStatus;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;
//import javax.sound.midi.MidiMessage;

import org.tritonus.share.midi.TDirectSynthesizer;




public class TDirectSynthesizerTestCase
extends TestCase
{
	public TDirectSynthesizerTestCase(String strName)
	{
		super(strName);
	}



	public void testNoteOn()
	throws Exception
	{
		checkMessage2(Type.NOTEON);
	}


	public void testNoteOff()
	throws Exception
	{
		checkMessage2(Type.NOTEOFF);
	}


	public void testPolyPressure()
	throws Exception
	{
		checkMessage2(Type.POLY_PRESSURE);
	}


	public void testControlChange()
	throws Exception
	{
		checkMessage2(Type.CONTROL_CHANGE);
	}


	public void testProgramChange()
	throws Exception
	{
		checkMessage1(Type.PROGRAM);
	}


	public void testChannelPressure()
	throws Exception
	{
		checkMessage1(Type.CHANNEL_PRESSURE);
	}


	public void testPitchbend()
	throws Exception
	{
		Synthesizer synth = new TestSynthesizer();
		synth.open();
		TestSynthesizer.TestChannel[] channels =
			(TestSynthesizer.TestChannel[]) synth.getChannels();
		try
		{
			Receiver r = synth.getReceiver();
			checkPitchbend(channels, r, 0, 0);
			checkPitchbend(channels, r, 5, 127);
			checkPitchbend(channels, r, 7, 128);
			checkPitchbend(channels, r, 15, 16383);
		}
		finally
		{
			synth.close();
		}
	}


	private void checkPitchbend(TestSynthesizer.TestChannel[] channels,
			Receiver r, int nChannel, int nBend)
	throws Exception
	{
		Type type = Type.PITCHBEND;
		ShortMessage shMsg = new ShortMessage();
		shMsg.setMessage(type.getCommand(),
				nChannel, nBend & 0x7F, nBend >> 7);
		resetResults(channels);
		r.send(shMsg, -1);
		checkResult(channels, nChannel, type,
				nBend, -1);
	}


	/**
	 * 
	 * @param bOn if true, note on is tested. If false, note off is tested. 
	 * @throws Exception
	 */
	private void checkMessage2(Type type)
	throws Exception
	{
		Synthesizer synth = new TestSynthesizer();
		synth.open();
		TestSynthesizer.TestChannel[] channels =
			(TestSynthesizer.TestChannel[]) synth.getChannels();
		try
		{
			Receiver r = synth.getReceiver();
			checkMessage(type, channels, r, 0, 17, 55);
			checkMessage(type, channels, r, 15, 0, 0);
			checkMessage(type, channels, r, 5, 127, 127);
		}
		finally
		{
			synth.close();
		}
	}


	/**
	 * 
	 * @param type
	 * @throws Exception
	 */
	private void checkMessage1(Type type)
	throws Exception
	{
		Synthesizer synth = new TestSynthesizer();
		synth.open();
		TestSynthesizer.TestChannel[] channels =
			(TestSynthesizer.TestChannel[]) synth.getChannels();
		try
		{
			Receiver r = synth.getReceiver();
			checkMessage(type, channels, r, 0, 57, 0);
			checkMessage(type, channels, r, 15, 0, 0);
			checkMessage(type, channels, r, 5, 127, 0);
		}
		finally
		{
			synth.close();
		}
	}


	private void checkMessage(Type type,
			TestSynthesizer.TestChannel[] channels,
			Receiver r, int nChannel, int nValue1, int nValue2)
	throws Exception
	{
		ShortMessage shMsg = new ShortMessage();
		shMsg.setMessage(type.getCommand(),
				nChannel, nValue1, nValue2);
		resetResults(channels);
		r.send(shMsg, -1);
		checkResult(channels, nChannel, type,
				nValue1, nValue2);
	}


	private void resetResults(TestSynthesizer.TestChannel[] channels)
	{
		for (int i = 0; i < channels.length; i++)
		{
			channels[i].resetValues();
		}
	}


	private void checkResult(TestSynthesizer.TestChannel[] channels,
			int channel, Type type, int value1, int value2)
	{
		for (int i = 0; i < channels.length; i++)
		{
			TestSynthesizer.TestChannel ch = channels[i];
			if (i == channel)
			{
				assertEquals("affected channel: type",
						type, ch.getType());
				assertEquals("affected channel: value1",
						value1, ch.getValue1());
				assertEquals("affected channel: value2",
						value2, ch.getValue2());
			}
			else
			{
				assertEquals("unaffected channel: type",
						Type.NONE, ch.getType());
				assertEquals("unaffected channel: value1",
						-1, ch.getValue1());
				assertEquals("unaffected channel: value2",
						-1, ch.getValue2());
			}
		}
	}


	private static class TestSynthesizer
	extends TDirectSynthesizer
	{
		private MidiChannel[]	m_channels;


		public TestSynthesizer()
		{
			// no MidiDevice.Info
			super(null);
			m_channels = new TestChannel[16];
			for (int i = 0; i < 16; i++)
			{
				m_channels[i] = new TestChannel(i);
			}
		}


		public int getMaxPolyphony()
		{
			return 16;
		}

		public long getLatency()
		{
			return 0;
		}


		public MidiChannel[] getChannels()
		{
			return m_channels;
		}


		public VoiceStatus[] getVoiceStatus()
		{
			return null;
		}


		public boolean isSoundbankSupported(Soundbank soundbank)
		{
			return false;
		}


		public boolean loadInstrument(Instrument instrument)
		{
			return false;
		}


		public void unloadInstrument(Instrument instrument)
		{
		}


		public boolean remapInstrument(Instrument from,
									   Instrument to)
		{
			return false;
		}


		public Soundbank getDefaultSoundbank()
		{
			return null;
		}


		public Instrument[] getAvailableInstruments()
		{
			return null;
		}


		public Instrument[] getLoadedInstruments()
		{
			return null;
		}


		public boolean loadAllInstruments(Soundbank soundbank)
		{
			return false;
		}


		public void unloadAllInstruments(Soundbank soundbank)
		{
		}


		public boolean loadInstruments(Soundbank soundbank,
									   Patch[] patchList)
		{
			return false;
		}


		public void unloadInstruments(Soundbank soundbank,
									  Patch[] patchList)
		{
		}


		public class TestChannel
		implements MidiChannel
		{
			private Type m_nType;
			private int m_nValue1;
			private int m_nValue2;


			public TestChannel(int nChannel)
			{
			}


			public void resetValues()
			{
				m_nType = Type.NONE;
				m_nValue1 = -1;
				m_nValue2 = -1;
			}


			public Type getType()
			{
				return m_nType;
			}


			public int getValue1()
			{
				return m_nValue1;
			}

			public int getValue2()
			{
				return m_nValue2;
			}

			public void allNotesOff()
			{
			}

			public void allSoundOff()
			{
			}

			public void controlChange(int nController, int nValue)
			{
				m_nType = Type.CONTROL_CHANGE;
				m_nValue1 = nController;
				m_nValue2 = nValue;
			}

			public int getChannelPressure()
			{
				return 0;
			}

			public int getController(int nController)
			{
				return 0;
			}

			public boolean getMono()
			{
				return false;
			}

			public boolean getMute()
			{
				return false;
			}

			public boolean getOmni()
			{
				return false;
			}

			public int getPitchBend()
			{
				return 0;
			}

			public int getPolyPressure(int nNoteNumber)
			{
				return 0;
			}

			public int getProgram()
			{
				return 0;
			}

			public boolean getSolo()
			{
				return false;
			}

			public boolean localControl(boolean bOn)
			{
				return false;
			}

			public void noteOff(int nNoteNumber, int nVelocity)
			{
				m_nType = Type.NOTEOFF;
				m_nValue1 = nNoteNumber;
				m_nValue2 = nVelocity;
			}

			public void noteOff(int nNoteNumber)
			{
			}

			public void noteOn(int nNoteNumber, int nVelocity)
			{
				m_nType = Type.NOTEON;
				m_nValue1 = nNoteNumber;
				m_nValue2 = nVelocity;
			}

			public void programChange(int nBank, int nProgram)
			{
				m_nType = Type.BANK_PROGRAM;
				m_nValue1 = nBank;
				m_nValue2 = nProgram;
			}

			public void programChange(int nProgram)
			{
				m_nType = Type.PROGRAM;
				m_nValue1 = nProgram;
				m_nValue2 = 0;
			}

			public void resetAllControllers()
			{
			}

			public void setChannelPressure(int nPressure)
			{
				m_nType = Type.CHANNEL_PRESSURE;
				m_nValue1 = nPressure;
				m_nValue2 = 0;
			}

			public void setMono(boolean bMono)
			{
			}

			public void setMute(boolean bMute)
			{
			}

			public void setOmni(boolean bOmni)
			{
			}

			public void setPitchBend(int nBend)
			{
				m_nType = Type.PITCHBEND;
				m_nValue1 = nBend;
			}

			public void setPolyPressure(int nNoteNumber, int nPressure)
			{
				m_nType = Type.POLY_PRESSURE;
				m_nValue1 = nNoteNumber;
				m_nValue2 = nPressure;
			}

			public void setSolo(boolean bSolo)
			{
			}
 		}
	}


	public static enum Type
	{
		NONE,
		CONTROL_CHANGE(ShortMessage.CONTROL_CHANGE),
		NOTEON(ShortMessage.NOTE_ON),
		NOTEOFF(ShortMessage.NOTE_OFF),
		PROGRAM(ShortMessage.PROGRAM_CHANGE),
		BANK_PROGRAM(ShortMessage.PROGRAM_CHANGE),
		PITCHBEND(ShortMessage.PITCH_BEND),
		POLY_PRESSURE(ShortMessage.POLY_PRESSURE),
		CHANNEL_PRESSURE(ShortMessage.CHANNEL_PRESSURE);
		
		private int m_nCommand;

		private Type()
		{
			this(0);
		}

		private Type(int nCommand)
		{
			m_nCommand = nCommand;
		}

		public int getCommand()
		{
			return m_nCommand;
		}
	}

			
}



/*** TDirectSynthesizerTestCase.java ***/
