package ddf.minim.ugens; import java.util.Arrays; import ddf.minim.UGen; /** * The Delay UGen is used to create delayed repetitions of the input audio. * One can control the delay time and amplification of the repetition. * One can also choose whether the repetition is fed back and/or the input is passed through. * * @example Synthesis/delayExample * * @author J Anderson Mills III */ public class Delay extends UGen { /** * where the incoming audio is patched * * @related Delay * @related UGen.UGenInput */ public UGenInput audio; /** * the time for delay between repetitions. * * @example Synthesis/delayExample * * @related setDelTime ( ) * @related Delay * @related UGen.UGenInput */ public UGenInput delTime; /** * the strength of each repetition compared to the previous. * often labeled as feedback on delay units. * * @example Synthesis/delayExample * * @related setDelAmp ( ) * @related Delay * @related UGen.UGenInput */ public UGenInput delAmp; // maximum delay time private float maxDelayTime; // the delay buffer based on maximum delay time private double[] delayBuffer; // how many sample frames does the delay buffer hold private int delayBufferFrames; // the index where we pull sound out of the delay buffer private int iBufferOut; // flag to include continual feedback. private boolean feedBackOn; // flag to pass the audio straight to the output. private boolean passAudioOn; // constructors /** * Constructs a Delay. Maximum delay time will be 0.25 seconds, * amplitude will be 0.5, and feedback will be off. */ public Delay() { this( 0.25f, 0.5f, false, true ); } /** * Constructs a Delay. Amplitude will be 0.5 and feedback will be off. * * @param maxDelayTime * float: is the maximum delay time for any one echo and the default echo time. */ public Delay( float maxDelayTime ) { this( maxDelayTime, 0.5f, false, true ); } /** * Constructs a Delay. Feedback will be off. * * @param maxDelayTime * float: is the maximum delay time for any one echo and the default echo time. * @param amplitudeFactor * float: is the amplification factor for feedback and should generally be from 0 to 1. */ public Delay( float maxDelayTime, float amplitudeFactor ) { this( maxDelayTime, amplitudeFactor, false, true ); } /** * Constructs a Delay. * * @param maxDelayTime * float: is the maximum delay time for any one echo and the default echo time. * @param amplitudeFactor * float: is the amplification factor for feedback and should generally be from 0 to 1. * @param feedBackOn * float: is a boolean flag specifying if the repetition continue to feed back. */ public Delay( float maxDelayTime, float amplitudeFactor, boolean feedBackOn ) { this( maxDelayTime, amplitudeFactor, feedBackOn, true ); } /** * Constructs a Delay. * * @param maxDelayTime * float: is the maximum delay time for any one echo and the default echo time. * @param amplitudeFactor * float: is the amplification factor for feedback and should generally be from 0 to 1. * @param feedBackOn * float: is a boolean flag specifying if the repetition continue to feed back. * @param passAudioOn * float: is a boolean value specifying whether to pass the input audio to the output as well. */ public Delay( float maxDelayTime, float amplitudeFactor, boolean feedBackOn, boolean passAudioOn ) { super(); // jam3: These can't be instantiated until the uGenInputs ArrayList // in the super UGen has been constructed audio = addAudio(); // time members this.maxDelayTime = maxDelayTime; delTime = addControl( maxDelayTime ); // amplitude member delAmp = addControl( amplitudeFactor ); // flags this.feedBackOn = feedBackOn; this.passAudioOn = passAudioOn; iBufferOut = 0; } /* * When the sample rate is changed the buffer needs to be resized. * Currently this causes the allocation of a completely new buffer, but * since a change in sampleRate will result in a change in the playback * speed of the sound in the buffer, I'm okay with this. */ protected void sampleRateChanged() { allocateDelayBuffer(); } protected void channelCountChanged() { allocateDelayBuffer(); } void allocateDelayBuffer() { delayBufferFrames = (int)( maxDelayTime*sampleRate() ); delayBuffer = new double [ delayBufferFrames*audio.channelCount() ]; iBufferOut = 0; } /** * Changes the time in between the echos to the value specified. * * @param delayTime * float: It can be up to the maxDelayTime specified. * The lowest it can be is 1/sampleRate. * * @example Synthesis/delayExample * * @related delTime * @related Delay */ public void setDelTime( float delayTime ) { delTime.setLastValue( delayTime ); } /** * Changes the feedback amplification of the echos. * * @param delayAmplitude * float: This should normally be between 0 and 1 for decreasing feedback. * Phase inverted feedback can be generated with negative numbers, but each echo * will be the inverse of the one before it. * * @example Synthesis/delayExample * * @related delAmp * @related Delay */ public void setDelAmp( float delayAmplitude ) { delAmp.setLastValue( delayAmplitude ); } @Override protected void uGenerate(float[] channels) { if ( delayBuffer == null || delayBuffer.length == 0 ) { Arrays.fill( channels, 0 ); return; } // how many samples do we delay the input int delay = (int)(delTime.getLastValue()*sampleRate()); int channelCount = channelCount(); for( int i = 0; i < channelCount; ++i ) { float in = audio.getLastValues()[i]; // pull sound out of the delay buffer int outSample = iBufferOut*channelCount + i; float out = delAmp.getLastValue()*(float)delayBuffer[ outSample ]; // eat it delayBuffer[ outSample ] = 0; // put sound into the buffer int inFrame = (iBufferOut+delay)%delayBufferFrames; int inSample = ( inFrame*channelCount + i); delayBuffer[ inSample ] = in; if ( feedBackOn ) { delayBuffer[ inSample ] += out; } if ( passAudioOn ) { out += in; } channels[i] = out; } iBufferOut = (iBufferOut + 1) % delayBufferFrames; } }