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;
}
}
}
}