package ddf.minim.ugens; import ddf.minim.UGen; /** * The TickRate UGen can be used to control the generation rate of the * UGen that is patched to it. It is equivalent to slowing down or * speeding up a record player. With a rate of 1, the patched UGen * will come through normally, but 0.5 will half as fast and sound * an octave lower, whereas 2 will be twice as fast and sound an * octave higher. Be aware that increasing the rate comes at the * expense of more computation because the patched UGen will be * ticked multiple times in one sample frame. Another limitation * is that a UGen patched to a TickRate should not be patched to * another UGen as well, the audio output will not be what you intend. * * @example Synthesis/tickRateExample * * @author Damien Di Fede * */ public class TickRate extends UGen { private UGen audio; /** * The rate that this ticks the UGen patched to it. * With a rate of 1, the patched UGen * will come through normally, but 0.5 will half as fast and sound * an octave lower, whereas 2 will be twice as fast and sound an * octave higher. * * @shortdesc The rate that this ticks the UGen patched to it. * * @example Synthesis/tickRateExample * * @related TickRate */ public UGenInput value; private float[] currentSample; private float[] nextSample; private float sampleCount; private boolean bInterpolate; /** * Constructs a TickRate. * The default rate is 1. * * @example Synthesis/tickRateExample * * @related TickRate */ public TickRate() { this( 1.f ); } /** * Constructs a TickRate. * * @example Synthesis/tickRateExample * * @param tickRate * float: the rate at which to tick a UGen patched to this * * @related TickRate * */ public TickRate( float tickRate ) { value = new UGenInput(InputType.CONTROL); value.setLastValue(tickRate); sampleCount = 0.f; currentSample = new float[2]; nextSample = new float[2]; bInterpolate = false; } /** * Enabled or disable sample interpolation. * When the rate is less than 1, that means * that TickRate will need to generate more * output sample frames than it gets from the * UGen patched to it. With interpolation turned * on, it will create these in-between sample frames * by interpolating between subsequent sample frames * generated by the patched UGen. With interpolation * turned off, this simply generates the most recent * sample frame generated by the patch UGen until it's * time to generate a new one. The result can be "crunchy" * sounding, with crunchiness increasing as the rate is reduced. * Interpolation is turned off be default because TickRate * is more computationally expensive with it on. * * @shortdesc Enabled or disable sample interpolation. * * @example Synthesis/tickRateExample * * @param doInterpolate * boolean: whether or not this TickRate should interpolate * * @related TickRate */ public final void setInterpolation( boolean doInterpolate ) { bInterpolate = doInterpolate; } /** * Returns whether or not this TickRate currently has interpolation on. * * @return boolean: is this TickRate interpolating * * @related TickRate */ public final boolean isInterpolating() { return bInterpolate; } @Override protected void addInput( UGen in ) { audio = in; audio.setChannelCount(currentSample.length); } @Override protected void removeInput( UGen in ) { if ( audio == in ) { audio = null; } } @Override protected void sampleRateChanged() { if ( audio != null ) { audio.setSampleRate(sampleRate()); } } @Override protected void channelCountChanged() { currentSample = new float[channelCount()]; nextSample = new float[channelCount()]; if ( audio != null ) { audio.setChannelCount(channelCount()); audio.tick(currentSample); audio.tick(nextSample); sampleCount = 0; } } @Override protected void uGenerate(float[] channels) { float sampleStep = value.getLastValue(); // for 0 or negative rate values, we just stop generating audio // effectively pausing generation of the patched ugen. if ( sampleStep <= 0.f ) { for(int i = 0; i < channels.length; ++i) { channels[i] = 0.f; } return; } if ( bInterpolate ) { for(int i = 0; i < channels.length; ++i) { float sampleDiff = nextSample[i] - currentSample[i]; channels[i] = currentSample[i] + sampleDiff * sampleCount; } } else { System.arraycopy(currentSample, 0, channels, 0, channels.length); } if ( audio != null ) { sampleCount += sampleStep; while( sampleCount >= 1.f ) { System.arraycopy(nextSample, 0, currentSample, 0, nextSample.length); audio.tick(nextSample); sampleCount -= 1.f; } } } }