package com.jsyn.examples;
import java.awt.BorderLayout;
import javax.swing.JApplet;
import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
import com.jsyn.instruments.WaveShapingVoice;
import com.jsyn.scope.AudioScope;
import com.jsyn.swing.JAppletFrame;
import com.jsyn.unitgen.Add;
import com.jsyn.unitgen.LineOut;
import com.jsyn.util.PseudoRandom;
import com.jsyn.util.VoiceAllocator;
import com.softsynth.jsyn.EqualTemperedTuning;
import com.softsynth.shared.time.TimeStamp;
/***************************************************************
* Play notes using a WaveShapingVoice. Allocate the notes using a
* VoiceAllocator.
*
* @author Phil Burk (C) 2010 Mobileer Inc
*/
public class ChebyshevSong extends JApplet implements Runnable
{
private static final long serialVersionUID = -7459137388629333223L;
private Synthesizer synth;
private Add mixer;
private LineOut lineOut;
private AudioScope scope;
private boolean go = false;
private PseudoRandom pseudo = new PseudoRandom();
private final static int MAX_VOICES = 8;
private final static int MAX_NOTES = 5;
private VoiceAllocator allocator;
private final static int scale[] = { 0, 2, 4, 7, 9 }; // pentatonic scale
/* Can be run as either an application or as an applet. */
public static void main( String args[] )
{
ChebyshevSong applet = new ChebyshevSong();
JAppletFrame frame = new JAppletFrame( "ChebyshevSong", applet );
frame.setSize( 640, 300 );
frame.setVisible( true );
frame.test();
}
/*
* Setup synthesis.
*/
public void start()
{
setLayout( new BorderLayout() );
synth = JSyn.createSynthesizer();
// Use a submix so we can show it on the scope.
synth.add( mixer = new Add() );
synth.add( lineOut = new LineOut() );
mixer.output.connect( 0, lineOut.input, 0 );
mixer.output.connect( 0, lineOut.input, 1 );
WaveShapingVoice[] voices = new WaveShapingVoice[MAX_VOICES];
for( int i = 0; i < MAX_VOICES; i++ )
{
WaveShapingVoice voice = new WaveShapingVoice();
synth.add( voice );
voice.usePreset( 0 );
voice.getOutput().connect( mixer.inputA );
voices[i] = voice;
}
allocator = new VoiceAllocator( voices );
// Start synthesizer using default stereo output at 44100 Hz.
synth.start();
lineOut.start();
// Use a scope to show the mixed output.
scope = new AudioScope( synth );
scope.addProbe( mixer.output );
scope.setTriggerMode( AudioScope.TriggerMode.NORMAL );
scope.getView().setControlsVisible( false );
add( BorderLayout.CENTER, scope.getView() );
scope.start();
/* Synchronize Java display. */
getParent().validate();
getToolkit().sync();
// start thread that plays notes
Thread thread = new Thread( this );
go = true;
thread.start();
}
public void stop()
{
// tell song thread to finish
go = false;
removeAll();
synth.stop();
}
double indexToFrequency( int index )
{
int octave = index / scale.length;
int temp = index % scale.length;
int pitch = scale[temp] + (12 * octave);
return EqualTemperedTuning.getMIDIFrequency( (int) (pitch + 16) );
}
private void noteOff( double time, int noteNumber )
{
allocator.noteOff( noteNumber, new TimeStamp( time ) );
}
private void noteOn( double time, int noteNumber )
{
double frequency = indexToFrequency( noteNumber );
double amplitude = 0.1;
TimeStamp timeStamp = new TimeStamp( time );
allocator.noteOn( noteNumber, frequency, amplitude, timeStamp );
allocator.setPort( noteNumber, "Range" , 0.7, synth.createTimeStamp() );
}
public void run()
{
// always choose a new song based on time&date
int savedSeed = (int) System.currentTimeMillis();
// calculate tempo
double duration = 0.2;
// set time ahead of any system latency
double advanceTime = 0.5;
// time for next note to start
double nextTime = synth.getCurrentTime() + advanceTime;
// note is ON for half the duration
double onTime = duration / 2;
int beatIndex = 0;
try
{
do
{
// on every measure, maybe repeat previous pattern
if( (beatIndex & 7) == 0 )
{
if( (Math.random() < (1.0 / 2.0)) )
pseudo.setSeed( savedSeed );
else if( (Math.random() < (1.0 / 2.0)) )
savedSeed = pseudo.getSeed();
}
// Play a bunch of random notes in the scale.
int numNotes = pseudo.choose( MAX_NOTES );
for( int i = 0; i < numNotes; i++ )
{
int noteNumber = pseudo.choose( 30 );
noteOn( nextTime, noteNumber );
noteOff( nextTime + onTime, noteNumber );
}
nextTime += duration;
beatIndex += 1;
// wake up before we need to play note to cover system latency
synth.sleepUntil( nextTime - advanceTime );
} while( go );
} catch( InterruptedException e )
{
System.err.println( "Song exiting. " + e );
}
}
}