[jsyn] Programming style question

jsyn at music.columbia.edu jsyn at music.columbia.edu
Wed Dec 7 11:23:12 EST 2005


Hi. I'm curious about what is considered to be the best method of
controlling SynthCircuit objects in real time, e.g. in response to a
MIDI keyboard. Clearly the note starts playing in real time when the key
is pressed, and will stop when the key is released, playing the release
portion of any envelopes.

However, the standard start() and stop() methods for controlling a
SynthCircuit don't quite fit here. Mainly because stop() will stop all
noise generators and modifiers before the release section of the
envelopes have completed. I wrote a sample SynthCircuit to play around
with this, and include it below. Basically I override start() and stop()
and queue the start portion of the envelope in start() and queue the
release portion in stop(). Start calls super.start() and stop calls
super.stop() scheduled some time in the future.

Looking at the documentation, it looks like it might be neater to use
setStage() with arguments 1 and 0 for start and stop. I'm going to write
a new version of this circuit using that method. But are there any other
instances of bad programming style in this example?

I'm going to be writing the second half of my JSyn module during
December, so it's likely that I'll be asking other basic questions over
the next little while.

Cheers,

Ross-c

import com.softsynth.jsyn.*;
import com.softsynth.jsyn.circuits.*;

/*
 * EnvelopeNoise.java is an example which demonstrates how to
 * create sounds with a variable length sustain portion. This
 * is similar to how a note on a synthesiser might sustain for
 * as long as you keep the key depressed.
 *
 * It's frequently important to use code like this when writing
 * realtime software synthesisers that respond to some sort of
 * external controller.
 *
 */

public class EnvelopeNoise extends SynthCircuit
{
  /*
   * We need to keep track of various envelope related properties
   * that that we can access them from the start and stop methods.
   *
   */

  private EnvelopePlayer envPlayer;
  private SynthEnvelope startEnv, endEnv;
  private double endEnvData[];

  /*
   * Single output - enveloped white noise.
   *
   */

  public SynthOutput output;

  /*
   * Constructor. The arguments are the envelope we want split into
   * two. "_startEnv" has the rate and level pairs for the envelope
   * up to the sustain portion. The last level of the _startEnv is
   * the sustain level.
   *
   * The endEnvelope has the rate and level pairs of the release
   * portion of the envelope.
   *
   */

  public EnvelopeNoise( double _startEnv[], double _endEnv[] )
  {
    startEnv = new SynthEnvelope( _startEnv );
    endEnv = new SynthEnvelope( _endEnv );
    endEnvData = _endEnv;

    WhiteNoise WN = new WhiteNoise();
    add( WN );
    addPort( output = WN.output );

    envPlayer = new EnvelopePlayer();
    add( envPlayer );

    envPlayer.output.connect( WN.amplitude );
  }

  /*
   * start method. Call super.start() so that the start() method of
   * the SynthCircuit class we inherit from can manage various things.
   * Then queue the start portion of the envelope.
   */

  public void start()
  {
    super.start();


    /*
     * Play the start portion of the envelope. Note that whatever the
     * last level of this envelope is will be sustained indefinitely until
     * some new envelope data is queued.
     *
     */

    envPlayer.envelopePort.queue( startEnv );
  }

  /*
   * stop method. Stop the current note playing. Note that the note will
   * not start playing immediately.
   *
   */

  public void stop()
  {

    /*
     * First work out how long it will take for the release portion of
     * the envelope to play.
     *
     */

    double timeToEnd = 0;

    for ( int index = 0; index < endEnvData.length; index = index + 2 )
    {
      timeToEnd = timeToEnd + endEnvData[index];
    }

    /*
     * ask the stop method of the superclass SynthCircuit to stop playing
     * at the appropriate future time. Note conversion to an integer
     * "tick" time some time in the future.
     *
     * Note also that (int) converts a double to an int.
     * E.g. "int i = (int) (5.7 * 6.2) will assign 35 to i.
     *
     */

    super.stop( Synth.getTickCount() + (int) (Synth.getTickRate() * timeToEnd ));


    /*
     * clear the envelope port of our player in case the start portion of
     * the envelope hasn't finished.
     *
     */

    envPlayer.envelopePort.clear();

    /*
     * Play the release portion.
     *
     */

    envPlayer.envelopePort.queue( endEnv );
  }

  /*
   * A main method that shows how the EnvelopeNoise class can be used.
   *
   */


  public static void main( String Args[] ) throws Exception
  {
    if ( Args.length != 1 )
    {
      throw new Exception( "Usage: java EnvelopeNoise <sustain time in ticks>" );
    }

    Synth.startEngine( 0 );

    /* Envelope split up into "start" and "release" portions. Note that
     * the sustain level is 1
     */

    double startEnv[] = { 0.5, 1 };
    double endEnv[] = { 1.8, 0 };

    EnvelopeNoise EnvN = new EnvelopeNoise( startEnv, endEnv );
    LineOut LO = new LineOut();

    EnvN.output.connect( 0, LO.input, 0 );
    EnvN.output.connect( 0, LO.input, 1 );

    /* start the noise playing.
     */

    EnvN.start();
    LO.start();

    /* sustain for a variable length of time.
     */

    Synth.sleepUntilTick( Integer.parseInt( Args[0] ) );

    /* request that the sound stops.
     */

    EnvN.stop();

    /* pause a bit longer so that we hear the release portion.
     * note that our argument controlling sustain should be shorter
     * than 5000 so that there is still some time left to wait for.
     */

    Synth.sleepUntilTick( 5000 );

    LO.stop();

    Synth.stopEngine();
  }
}



More information about the JSyn mailing list