package jmslexamples.jsyn;
import java.applet.Applet;
import java.awt.GridLayout;

import com.softsynth.jmsl.JMSL;
import com.softsynth.jmsl.jsyn.JSynMusicDevice;
import com.softsynth.jsyn.*;
import com.softsynth.jsyn.view102.SynthScope;
import com.softsynth.jsyn.view102.UsageDisplay;

/** 
 * A small composition using the Java Audio Synthesiser.
 * Each voice has an FM pair.  The Carrier frequency is set
 * to the last Frequency times a whole number ratio. The Modulator
 * Frequency is set to the last Frequency.
 * This example extends FMNoodler by adding envelopes.

 * @author Phil Burk
 */
/*
 * (C) 1997 Phil Burk
 * All Rights Reserved
 */

class FMNoodlerEnv extends FMNoodler {
	SynthEnvelope myEnvData;
	public EnvelopePlayer myEnv;

	public FMNoodlerEnv(double duration, int flags, double ampl) throws SynthException {
		super(duration, flags);
		System.out.println("FMNoodlerEnv(dur=" + duration + ", " + flags);

		/* Create an envelope 1/10th length as duration. */
		double[] data = { duration * 0.01, ampl, /* duration,value pair for frame[0] */
			duration * 0.20, 0.4 * ampl, /* duration,value pair for frame[1] */
			duration * 0.80, 0.0 /* duration,value pair for frame[2] */
		};
		int numFrames = data.length / 2;
		myEnvData = new SynthEnvelope(numFrames);
		myEnvData.write(0, data, 0, numFrames);

		/* Create an envelope playing unit generator. */
		myEnv = new EnvelopePlayer();

		/* Add myEnv before fmPair is started so that it gets compiled into circuit. */
		fmPair.add(myEnv);

		/* Use envelope to control oscillator amplitude. */
		myEnv.output.connect(fmPair.amplitude);
	}

	/* Override repeat() method for MusicJob to play a note. */
	public double repeat(double playTime) throws InterruptedException {
		//		System.out.println("repeat(" + playTime + ")");
		int itime = (int) JMSL.clock.timeToNative(playTime);
		try {
			/* Randomly skip a note. */
			if (Math.random() < 0.8) {
				myEnv.envelopePort.clear(itime);
				/* Make FMPair stop when envelope finishes. */
				myEnv.envelopePort.queue(itime, myEnvData, 0, myEnvData.getNumFrames(), Synth.FLAG_AUTO_STOP);
			}
			playTime = super.repeat(playTime);
		}
		catch (SynthException e) {
			SynthAlert.showError(null, e);
		}
		return playTime;
	}

}

/* *************************************************************** */
public class TuningNoodler1 extends Applet {
	public static final int NUM_NOODLERS = 4;
	public static final double BASE_PERIOD = 0.1; /* fundamental note delay in seconds */
	FMNoodlerEnv[] noodlers = new FMNoodlerEnv[NUM_NOODLERS];
	SynthScope scope;

	/* Can be run as either an application or as an applet. */
	public static void main(String args[]) {
		TuningNoodler1 applet = new TuningNoodler1();
		AppletFrame frame = new AppletFrame("FM Blips with Dynamic Tuning.", applet);
		frame.setSize(600, 400);
		frame.show();
		/* Begin test after frame opened so that DirectSound will use Java window. */
		frame.test();
	}

	/*
	 * Setup synthesis.
	 */
	public void start() {
		setLayout(new GridLayout(0, 1));
		JSynMusicDevice.instance().open();

		/* Create several noodlers that run at different rates. */
		double ampl = 0.9 / NUM_NOODLERS; /* Guarantee no clipping. */
		for (int i = 0; i < NUM_NOODLERS; i++) {
			noodlers[i] = new FMNoodlerEnv((1 + i) * BASE_PERIOD, FMNoodler.LEFT + FMNoodler.RIGHT, ampl);
			noodlers[i].setRepeats(4000);
		}

		/* Start execution of units staggered in time. */
		double startTime = JMSL.now();
		for (int i = 0; i < NUM_NOODLERS; i++) {
			noodlers[i].launch(startTime);
			startTime += 10 * BASE_PERIOD;
		}

		/* Add a button that polls and displays the current CPU usage. */
		add(new UsageDisplay());
		/* Synchronize Java display. */
		getParent().validate();
		getToolkit().sync();
	}

	public void stop() {
		try {
			removeAll();
			for (int i = 0; i < NUM_NOODLERS; i++) {
				noodlers[i].finishAll();
				noodlers[i].waitForDone();
			}
			JMSL.closeMusicDevices();
		}
		catch (Exception e) {
			System.out.println("Exception when stopping! " + e);
		}
	}
}