package com.jsyn.examples;
import java.awt.GridLayout;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.JApplet;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
import com.jsyn.data.FloatSample;
import com.jsyn.devices.AudioDeviceFactory;
import com.jsyn.ports.QueueDataCommand;
import com.jsyn.swing.DoubleBoundedRangeModel;
import com.jsyn.swing.DoubleBoundedRangeSlider;
import com.jsyn.swing.JAppletFrame;
import com.jsyn.swing.PortControllerFactory;
import com.jsyn.unitgen.LineOut;
import com.jsyn.unitgen.VariableRateDataReader;
import com.jsyn.unitgen.VariableRateMonoReader;
import com.jsyn.unitgen.VariableRateStereoReader;
import com.jsyn.util.SampleLoader;
/**
* Play a sample from a WAV file using JSyn. Use a crossfade to play a loop at
* an arbitrary position.
*
* @author Phil Burk (C) 2010 Mobileer Inc
*
*/
public class PlaySampleCrossfade extends JApplet
{
private static final double LOOP_START_FRACTION = 0.2;
private Synthesizer synth;
private VariableRateDataReader samplePlayer;
private LineOut lineOut;
private FloatSample sample;
private DoubleBoundedRangeModel rangeModelSize;
private DoubleBoundedRangeModel rangeModelCrossfade;
private int loopStartFrame;
public void init()
{
URL sampleFile;
try
{
sampleFile = new URL(
"http://www.softsynth.com/samples/Clarinet.wav" );
} catch( MalformedURLException e2 )
{
e2.printStackTrace();
return;
}
synth = JSyn.createSynthesizer( AudioDeviceFactory.createAudioDeviceManager( true ));
try
{
// Add an output mixer.
synth.add( lineOut = new LineOut() );
// Load the sample and display its properties.
SampleLoader.setJavaSoundPreferred( false );
sample = SampleLoader.loadFloatSample( sampleFile );
System.out.println( "Sample has: channels = "
+ sample.getChannelsPerFrame() );
System.out.println( " frames = "
+ sample.getNumFrames() );
System.out.println( " rate = "
+ sample.getFrameRate() );
System.out.println( " loopStart = "
+ sample.getSustainBegin() );
System.out.println( " loopEnd = "
+ sample.getSustainEnd() );
if( sample.getChannelsPerFrame() == 1 )
{
synth.add( samplePlayer = new VariableRateMonoReader() );
samplePlayer.output.connect( 0, lineOut.input, 0 );
}
else if( sample.getChannelsPerFrame() == 2 )
{
synth.add( samplePlayer = new VariableRateStereoReader() );
samplePlayer.output.connect( 0, lineOut.input, 0 );
samplePlayer.output.connect( 1, lineOut.input, 1 );
}
else
{
throw new RuntimeException(
"Can only play mono or stereo samples." );
}
samplePlayer.rate.set( sample.getFrameRate() );
} catch( IOException e1 )
{
e1.printStackTrace();
}
// Start at arbitrary position near beginning of sample.
loopStartFrame = (int) (sample.getNumFrames() * LOOP_START_FRACTION );
// Arrange the faders in a stack.
setLayout( new GridLayout( 0, 1 ) );
samplePlayer.rate.setup( 4000.0, sample.getFrameRate(),
sample.getFrameRate() * 2.0 );
add( PortControllerFactory
.createExponentialPortSlider( samplePlayer.rate ) );
// Use fader to select arbitrary loop size.
rangeModelSize = new DoubleBoundedRangeModel( "LoopSize", 10000, 0.01,
(1.0 - LOOP_START_FRACTION), 0.5 );
rangeModelSize.addChangeListener( new ChangeListener()
{
public void stateChanged( ChangeEvent e )
{
queueNewLoop();
}
} );
add( new DoubleBoundedRangeSlider( rangeModelSize, 3 ) );
// Use fader to set the size of the crossfade region.
rangeModelCrossfade = new DoubleBoundedRangeModel( "Crossfade", 1000,
0.0, 1000.0, 0.0 );
rangeModelCrossfade.addChangeListener( new ChangeListener()
{
public void stateChanged( ChangeEvent e )
{
queueNewLoop();
}
} );
add( new DoubleBoundedRangeSlider( rangeModelCrossfade, 3 ) );
validate();
}
private void queueNewLoop()
{
int loopSize = (int) (sample.getNumFrames() * rangeModelSize
.getDoubleValue());
if( (loopStartFrame + loopSize) > sample.getNumFrames() )
{
loopSize = sample.getNumFrames() - loopStartFrame;
}
int crossFadeSize = (int) (rangeModelCrossfade.getDoubleValue());
// For complex queuing operations, create a command and then customize it.
QueueDataCommand command = samplePlayer.dataQueue
.createQueueDataCommand( sample, loopStartFrame, loopSize );
command.setNumLoops( -1 );
command.setSkipIfOthers( true );
command.setCrossFadeIn( crossFadeSize );
System.out.println( "Queue: " + loopStartFrame + ", #" + loopSize
+ ", X=" + crossFadeSize );
synth.queueCommand( command );
}
public void start()
{
// Start synthesizer using default stereo output at 44100 Hz.
synth.start();
// Start the LineOut. It will pull data from the oscillator.
lineOut.start();
// Queue attack portion of sample.
samplePlayer.dataQueue.queue( sample, 0, loopStartFrame );
queueNewLoop();
}
public void stop()
{
synth.stop();
synth.stop();
}
/* Can be run as either an application or as an applet. */
public static void main( String args[] )
{
PlaySampleCrossfade applet = new PlaySampleCrossfade();
JAppletFrame frame = new JAppletFrame( "PlaySampleCrossfade", applet );
frame.setSize( 440, 300 );
frame.setVisible( true );
frame.test();
}
}