package ddf.minim.ugens;
import java.util.Arrays;
import ddf.minim.UGen;
//Moog 24 dB/oct resonant lowpass VCF
//References: CSound source code, Stilson/Smith CCRMA paper.
//Modified by paul.kellett@maxim.abel.co.uk July 2000
//Java implementation by Damien Di Fede September 2010
/**
* MoogFilter is a digital model of a Moog 24 dB/octave resonant VCF.
* It can be set to low pass, high pass, or band pass using the
* MoogFilter.Type enumeration. More generally, a filter is used to
* remove certain ranges of the audio spectrum from a sound.
* A low pass filter will allow frequencies below the cutoff frequency
* to be heard, a high pass filter allows frequencies above the cutoff
* frequency to be heard, a band pass filter will allow frequencies
* to either side of the center frequency to be heard. With MoogFilter,
* the cutoff frequency and the center frequency are set using the
* <code>frequency</code> input. Because this is a <i>resonant</i>
* filter, it means that frequencies close to the cutoff of center frequency
* will become slighly emphasized, depending on the value of the
* <code>resonance</code> input. The resonance of the filter has a
* range from 0 to 1, where as the resonance approaches 1 the filter will
* begin to "ring" at the cutoff frequency.
*
* @example Synthesis/moogFilterExample
*
* @related UGen
*
* @author Damien Di Fede
*
*/
public class MoogFilter extends UGen
{
/**
* The MoogFilter.Type enumeration is used to set
* the filter mode of a MoogFilter. HP is high pass,
* LP is low pass, and BP is band pass.
*
* @example Synthesis/moogFilterExample
*
* @related type
* @related MoogFilter
*
* @nosuperclasses
*/
public enum Type
{
/**
* The value representing high pass.
*
* @related type
*/
HP,
/**
* The value representing low pass.
*
* @related type
*/
LP,
/**
* The value representing band pass.
*
* @related type
*/
BP
}
/**
* The main audio input where the the UGen
* you want to filter should be patched.
*
* @related MoogFilter
* @related UGen.UGenInput
*/
public UGenInput audio;
/**
* The cutoff (or center) frequency of the filter,
* expressed in Hz.
*
* @example Synthesis/moogFilterExample
*
* @related MoogFilter
* @related UGen.UGenInput
*/
public UGenInput frequency;
/**
* The resonance of the filter, expressed as a normalized value [0,1].
*
* @example Synthesis/moogFilterExample
*
* @related MoogFilter
* @related UGen.UGenInput
*/
public UGenInput resonance;
/**
* The current type of this filter: low pass, high pass, or band pass.
*
* @example Synthesis/moogFilterExample
*
* @related MoogFilter.Type
*/
public Type type;
private float coeff[][]; // filter buffers (beware denormals!)
/**
* Creates a low pass filter.
*
* @param frequencyInHz
* float: the cutoff frequency for the filter
* @param normalizedResonance
* float: the resonance of the filter [0,1]
*/
public MoogFilter( float frequencyInHz, float normalizedResonance )
{
this( frequencyInHz, normalizedResonance, Type.LP );
}
/**
* Creates a filter of the type specified.
*
* @param frequencyInHz
* float: the cutoff frequency for the filter
* @param normalizedResonance
* float: the resonance of the filter [0,1]
* @param filterType
* the type of the filter: MoogFilter.Type.HP (high pass),
* MoogFitler.Type.LP (low pass), or MoogFilter.Type.BP (band pass)
*/
public MoogFilter(float frequencyInHz, float normalizedResonance, Type filterType )
{
super();
audio = new UGenInput( InputType.AUDIO );
frequency = new UGenInput( InputType.CONTROL );
resonance = new UGenInput( InputType.CONTROL );
type = filterType;
frequency.setLastValue( frequencyInHz );
resonance.setLastValue( constrain( normalizedResonance, 0.f, 1.f ) );
coeff = new float[channelCount()][5];
}
protected void channelCountChanged()
{
if ( coeff == null || coeff.length != channelCount() )
{
coeff = new float[channelCount()][5];
}
}
protected void uGenerate(float[] out)
{
// Set coefficients given frequency & resonance [0.0...1.0]
float t1, t2; // temporary buffers
float normFreq = frequency.getLastValue() / ( sampleRate() * 0.5f );
float rez = constrain( resonance.getLastValue(), 0.f, 1.f );
float q = 1.0f - normFreq;
float p = normFreq + 0.8f * normFreq * q;
float f = p + p - 1.0f;
q = rez * ( 1.0f + 0.5f * q * ( 1.0f - q + 5.6f * q * q ) );
float[] input = audio.getLastValues();
for ( int i = 0; i < channelCount(); ++i )
{
// Filter (in [-1.0...+1.0])
float[] b = coeff[i];
float in = constrain( input[i], -1, 1 ); // hard clip
in -= q * b[4]; // feedback
t1 = b[1];
b[1] = ( in + b[0] ) * p - b[1] * f;
t2 = b[2];
b[2] = ( b[1] + t1 ) * p - b[2] * f;
t1 = b[3];
b[3] = ( b[2] + t2 ) * p - b[3] * f;
b[4] = ( b[3] + t1 ) * p - b[4] * f;
b[4] = b[4] - b[4] * b[4] * b[4] * 0.166667f; // clipping
// inelegantly squash denormals
if ( Float.isNaN( b[4] ) )
{
Arrays.fill( b, 0 );
}
b[0] = in;
switch( type )
{
case HP:
out[i] = in - b[4];
break;
case LP:
out[i] = b[4];
break;
case BP:
out[i] = 3.0f * (b[3] - b[4]);
}
}
}
private float constrain( float value, float min, float max )
{
if ( value < min ) return min;
if ( value > max ) return max;
return value;
}
}