|
import jm.music.rt.RTLine; import jm.audio.Instrument; import jm.music.data.Note; import javax.swing.*; public class BassLine extends RTLine { private Note n = new Note(36, 0.5); private int pitch = 36; private int[] intervals = {0, 0, 0, 4, 7, 10, 12}; private double panPosition = 0.5; /** Constructor */ public BassLine (Instrument[] instArray) { super(instArray); } /** * Generate the next note when requested. */ public synchronized Note getNextNote() { n.setPitch(pitch + intervals[(int)(Math.random() * intervals.length)]); n.setDynamic((int)(Math.random()* 80 + 45)); n.setPan(panPosition); n.setRhythmValue((int)(Math.random() * 2 + 1) * 0.25); n.setDuration(n.getRhythmValue() * 0.9); return n; } /** Allow other classes to set the notes pan value */ public void setPanValue(double p) { this.panPosition = p; } // added for control change public synchronized void externalAction(Object obj, int actionNumber){ if(actionNumber == 1){ JSlider slider = (JSlider)obj; double filterCutoff = (double)(slider.getValue() * 100); for(int i=0;i<inst.length;i++){ double[] values = {filterCutoff}; inst[i].setController(values); } } } } |
In this example the
RTComposition class
creates a graphical user interface and sends commands
to the RTMixer
for broadcasting to the RTLines when the interface is
updated, by a
user moving a slider.
import jm.JMC;
|
To make this all work, we need to have an instrument
class that will
respond to the control change messages being passed
around. The key to
this is having the instrument implement a setController()
method that is
called by the RTLine
sub
class. An important consideration is that the audio
object, in this
case the Filter instance called filt,
needs to be declared as an instance variable so that is
it visible to
the setController()
method.
An array is passed to this method which allows for a
group of parameter
changes to be passed at one time. In this case there is
only one value,
so it is accessed as the zeroth index in the array.
import jm.audio.Instrument; import jm.audio.io.*; import jm.audio.synth.*; import jm.music.data.Note; import jm.audio.AudioObject; /** * A monophonic sawtooth waveform instrument implementation * which includes a static low pass filter. * @author Andrew Brown */ public final class SawLPFInstRT2 extends Instrument{ //---------------------------------------------- // Instance variables //---------------------------------------------- private int sampleRate; private int filterCutoff; private int channels; private Filter filt; //---------------------------------------------- // Constructor //---------------------------------------------- public SawLPFInstRT2(){ this(44100); } /** * Basic default constructor to set an initial * sampling rate and use a default cutoff. * @param sampleRate */ public SawLPFInstRT2(int sampleRate){ this(sampleRate, 1000); } /** * Constructor that sets sample rate and the filter cutoff frequency. * @param sampleRate The number of samples per second (quality) * @param filterCutoff The frequency above which overtones are cut */ public SawLPFInstRT2(int sampleRate, int filterCutoff){ this(sampleRate, filterCutoff, 1); } /** * Constructor that sets sample rate, filter cutoff frequency, and channels. * @param sampleRate The number of samples per second (quality) * @param filterCutoff The frequency above which overtones are cut * @param channels 1 = mono, 2 = stereo. */ public SawLPFInstRT2(int sampleRate, int filterCutoff, int channels){ this.sampleRate = sampleRate; this.filterCutoff = filterCutoff; this.channels = channels; } //---------------------------------------------- // Methods //---------------------------------------------- /** * Initialisation method used to build the objects that * this instrument will use and specify their interconnections. */ public void createChain(){ Oscillator wt = new Oscillator(this, Oscillator.SAWTOOTH_WAVE, this.sampleRate, this.channels); filt = new Filter(wt, this.filterCutoff, Filter.LOW_PASS); Envelope env = new Envelope(filt, new double[] {0.0, 0.0, 0.02, 1.0, 0.2, 0.5, 0.8, 0.3, 1.0, 0.0}); Volume vol = new Volume(env); StereoPan pan = new StereoPan(vol); } /** Changes the specified controller when called */ public void setController(double[] controlValues){ filt.setCutOff(controlValues[0]); } } |
While this message passing process is somewhat
complicated to
start with you will find that it is very flexible and
should allow for
quite sophisticated real time control of audio
parameters.
© 2004 Andrew Brown |
|
|
|
|
|