package com.jsyn.examples;
import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
import com.jsyn.instruments.SubtractiveSynthVoice;
import com.jsyn.unitgen.LineOut;
import com.jsyn.util.VoiceAllocator;
import com.softsynth.shared.time.TimeStamp;
/**
* Play chords and melody using the VoiceAllocator.
*
* @author Phil Burk (C) 2009 Mobileer Inc
*
*/
public class PlayChords
{
private static final int MAX_VOICES = 8;
private Synthesizer synth;
private VoiceAllocator allocator;
private LineOut lineOut;
/** Number of seconds to generate music in advance of presentation-time. */
private double advance = 0.2;
private double secondsPerBeat = 0.6;
// on time over note duration
private double dutyCycle = 0.8;
private double measure = secondsPerBeat * 4.0;
private SubtractiveSynthVoice[] voices;
private void test()
{
synth = JSyn.createSynthesizer();
// Add an output.
synth.add( lineOut = new LineOut() );
voices = new SubtractiveSynthVoice[MAX_VOICES];
for( int i = 0; i < MAX_VOICES; i++ )
{
SubtractiveSynthVoice voice = new SubtractiveSynthVoice();
synth.add( voice );
voice.getOutput().connect( 0, lineOut.input, 0 );
voice.getOutput().connect( 0, lineOut.input, 1 );
voices[i] = voice;
}
allocator = new VoiceAllocator( voices );
// Start synthesizer using default stereo output at 44100 Hz.
synth.start();
// We only need to start the LineOut. It will pull data from the
// voices.
lineOut.start();
// Get synthesizer time in seconds.
double timeNow = synth.getCurrentTime();
// Advance to a near future time so we have a clean start.
double time = timeNow + 1.0;
try
{
int tonic = 60 - 12;
for( int i = 0; i < 4; i++ )
{
playMajorMeasure1( time, tonic );
time += measure;
catchUp( time );
playMajorMeasure1( time, tonic + 4 );
time += measure;
catchUp( time );
playMajorMeasure1( time, tonic + 7 );
time += measure;
catchUp( time );
playMinorMeasure1( time, tonic + 2 ); // minor chord
time += measure;
catchUp( time );
}
time += secondsPerBeat;
catchUp( time );
} catch( InterruptedException e )
{
e.printStackTrace();
}
// Stop everything.
synth.stop();
}
private void playMinorMeasure1( double time, int base )
throws InterruptedException
{
int p1 = base;
int p2 = base + 3;
int p3 = base + 7;
playChord1( time, p1, p2, p3 );
playNoodle1( time, p1 + 24, p2 + 24, p3 + 24 );
}
private void playMajorMeasure1( double time, int base )
throws InterruptedException
{
int p1 = base;
int p2 = base + 4;
int p3 = base + 7;
playChord1( time, p1, p2, p3 );
playNoodle1( time, p1 + 24, p2 + 24, p3 + 24 );
}
private void playNoodle1( double time, int p1, int p2, int p3 )
{
double secondsPerNote = secondsPerBeat * 0.5;
for( int i = 0; i < 8; i++ )
{
int p = pickFromThree( p1, p2, p3 );
noteOn( time, p );
noteOff( time + dutyCycle * secondsPerNote, p );
time += secondsPerNote;
}
}
private int pickFromThree( int p1, int p2, int p3 )
{
int r = (int) (Math.random() * 3.0);
if( r < 1 )
return p1;
else if( r < 2 )
return p2;
else
return p3;
}
private void playChord1( double time, int p1, int p2, int p3 )
throws InterruptedException
{
double dur = dutyCycle * secondsPerBeat;
playTriad( time, dur, p1, p2, p3 );
time += secondsPerBeat;
playTriad( time, dur, p1, p2, p3 );
time += secondsPerBeat;
playTriad( time, dur * 0.25, p1, p2, p3 );
time += secondsPerBeat * 0.25;
playTriad( time, dur * 0.25, p1, p2, p3 );
time += secondsPerBeat * 0.75;
playTriad( time, dur, p1, p2, p3 );
time += secondsPerBeat;
}
private void playTriad( double time, double dur, int p1, int p2, int p3 )
throws InterruptedException
{
noteOn( time, p1 );
noteOn( time, p2 );
noteOn( time, p3 );
double offTime = time + dur;
noteOff( offTime, p1 );
noteOff( offTime, p2 );
noteOff( offTime, p3 );
}
private void catchUp( double time ) throws InterruptedException
{
synth.sleepUntil( time - advance );
}
private void noteOff( double time, int noteNumber )
{
allocator.noteOff( noteNumber, new TimeStamp( time ) );
}
private void noteOn( double time, int noteNumber )
{
double frequency = convertPitchToFrequency( noteNumber );
double amplitude = 0.2;
TimeStamp timeStamp = new TimeStamp( time );
allocator.noteOn( noteNumber, frequency, amplitude, timeStamp );
}
/**
* Calculate frequency in Hertz based on MIDI pitch. Middle C is 60.0. You
* can use fractional pitches so 60.5 would give you a pitch half way
* between C and C#.
*/
double convertPitchToFrequency( double pitch )
{
final double concertA = 440.0;
return concertA * Math.pow( 2.0, ((pitch - 69) * (1.0 / 12.0)) );
}
public static void main( String[] args )
{
new PlayChords().test();
}
}