package ddf.minim.ugens;
import ddf.minim.UGen;
/**
* A UGen that can generate White, Pink, or Red/Brown noise.
*
* @example Synthesis/noiseExample
*
* @author Anderson Mills, Damien Di Fede
*
* @related UGen
* @related Noise.Tint
*/
public class Noise extends UGen
{
/**
* An enumeration used to specify the tint of a Noise UGen.
*
* @example Synthesis/noiseTintExample
*
* @nosuperclasses
*
* @related Noise
*/
public enum Tint { WHITE, PINK, RED, BROWN };
/**
* Patch to this to control the amplitude of the noise with another UGen.
*
* @related Noise
*/
public UGenInput amplitude;
/**
* Patch to this to offset the value of the noise by a fixed value.
*
* @related Noise
*/
public UGenInput offset;
// the type of noise
private Tint tint;
// the last output value
private float lastOutput;
// cutoff frequency for brown/red noise
private float brownCutoffFreq = 100.0f;
// alpha filter coefficient for brown/red noise
private float brownAlpha;
// amplitude correction for brown noise;
private float brownAmpCorr = 6.2f;
/**
* Constructor for white noise.
* By default, the amplitude will be 1 and the tint will be WHITE.
*/
public Noise()
{
this( 1.0f, 0.f, Tint.WHITE );
}
/**
* Constructor for white noise of the specified amplitude.
*
* @param amplitude
* float: the amplitude of the noise
*/
public Noise( float amplitude )
{
this( amplitude, 0.f, Tint.WHITE ) ;
}
/**
* Constructor for noise of the specified tint with an amplitude of 1.0.
*
* @param noiseType
* Noise.Tint: specifies the tint of the noise
* (Noise.Tint.WHITE, Noise.Tint.PINK, Noise.Tint.RED, Noise.Tint.BROWN)
*/
public Noise( Tint noiseType )
{
this( 1.0f, 0.f, noiseType ) ;
}
/**
* Constructor for noise of a specific tint with a specified amplitude.
*
* @param amplitude
* float: the amplitude of the noise
* @param noiseType
* Noise.Tint: specifies the tint of the noise
* (Noise.Tint.WHITE, Noise.Tint.PINK, Noise.Tint.RED, Noise.Tint.BROWN)
*/
public Noise(float amplitude, Tint noiseType)
{
this(amplitude, 0.f, noiseType);
}
/**
* Constructor for noise of a specific tint with a specified amplitude and offset.
* @param amplitude
* float: the amplitude of the noise
* @param offset
* float: the value that should be added to the noise to offset the "center"
* @param noiseType
* Noise.Tint: specifies the tint of the noise
* (Noise.Tint.WHITE, Noise.Tint.PINK, Noise.Tint.RED, Noise.Tint.BROWN)
*/
public Noise(float amplitude, float offset, Tint noiseType)
{
this.amplitude = addControl(amplitude);
this.offset = addControl(offset);
lastOutput = 0f;
tint = noiseType;
if ( tint == Tint.PINK )
{
initPink();
}
}
/**
* Set the Noise.Tint to use.
*
* @param noiseType
* Noise.Tint: specifies the tint of the noise
* (Noise.Tint.WHITE, Noise.Tint.PINK, Noise.Tint.RED, Noise.Tint.BROWN)
*
* @related Noise
* @related Noise.Tint
*/
public void setTint( Tint noiseType )
{
if ( tint != noiseType )
{
if ( noiseType == Tint.PINK )
{
initPink();
}
tint = noiseType;
}
}
/**
* Returns the current Noise.Tint in use
*
* @return Noise.Tint: the current tint of the noise
* (Noise.Tint.WHITE, Noise.Tint.PINK, Noise.Tint.RED, Noise.Tint.BROWN)
*
* @related Noise
* @related Noise.Tint
*/
public final Tint getTint()
{
return tint;
}
@Override
protected void sampleRateChanged()
{
float dt = 1.0f/sampleRate();
float RC = 1.0f/( 2.0f*(float)Math.PI*brownCutoffFreq );
brownAlpha = dt/( RC + dt );
}
@Override
protected void uGenerate(float[] channels)
{
// start with our base amplitude
float outAmp = amplitude.getLastValue();
float n;
switch (tint)
{
// BROWN is a 1/f^2 spectrum (20db/decade, 6db/octave).
// There is some disagreement as to whether
// brown and red are the same, but here they are.
case BROWN :
case RED :
// I admit that I'm using the filter coefficients and
// amplitude correction from audacity, a great audio editor.
n = outAmp*(2.0f*(float)Math.random() - 1.0f);
n = brownAlpha*n + ( 1 - brownAlpha )*lastOutput;
lastOutput = n;
n *= brownAmpCorr;
break;
// PINK noise has a 10db/decade (3db/octave) slope
case PINK :
n = outAmp*pink();
break;
case WHITE :
default :
n = outAmp*(2.0f*(float)Math.random() - 1.0f);
break;
}
n += offset.getLastValue();
for(int i = 0; i < channels.length; i++)
{
channels[i] = n;
}
}
// The code below (including comments) is taken directly from ddf's old PinkNoise.java code
// This is the Voss algorithm for creating pink noise
private int maxKey, key, range;
private float whiteValues[];
private float maxSumEver;
private void initPink()
{
maxKey = 0x1f;
range = 128;
maxSumEver = 90;
key = 0;
whiteValues = new float[6];
for (int i = 0; i < 6; i++)
whiteValues[i] = ((float) Math.random() * Long.MAX_VALUE) % (range / 6);
}
// return a pink noise value
private float pink()
{
int last_key = key;
float sum;
key++;
if (key > maxKey) key = 0;
// Exclusive-Or previous value with current value. This gives
// a list of bits that have changed.
int diff = last_key ^ key;
sum = 0;
for (int i = 0; i < 6; i++)
{
// If bit changed get new random number for corresponding
// white_value
if ((diff & (1 << i)) != 0)
{
whiteValues[i] = ((float) Math.random() * Long.MAX_VALUE) % (range / 6);
}
sum += whiteValues[i];
}
if (sum > maxSumEver) maxSumEver = sum;
sum = 2f * (sum / maxSumEver) - 1f;
return sum;
}
}