package ddf.minim.ugens; import java.util.ArrayList; import java.util.Arrays; import ddf.minim.AudioSignal; import ddf.minim.Minim; import ddf.minim.UGen; /** * A Summer allows you to sum the outputs of multiple UGens to be sent further * down the chain. Unlike most UGen effects, you can patch more than one UGen to * a Summer. * * @example Synthesis/summerExample * * @author Damien Di Fede * */ public class Summer extends UGen implements AudioSignal { private ArrayList<UGen> m_ugens; private float[] m_tickBuffer; /** * Constructs a Summer that you can patch multiple UGens to. * */ public Summer() { m_ugens = new ArrayList<UGen>(); } // ddf: override because everything that patches to us // goes into our list. then when we generate a sample // we'll sum the audio generated by all of the ugens patched to us. @Override protected void addInput(UGen input) { // Minim.debug( "Bus::addInput - Adding " + input + " to the m_ugens list of " + this ); // it needs to know how many channels of audio we expect // we set the channel count before adding because concurrency means // that we might try to tick input between the add finishing and // setAudioChannelCount completing. input.setChannelCount( channelCount() ); synchronized( m_ugens ) { m_ugens.add( input ); } } @Override protected void removeInput(UGen input) { Minim.debug( "Bus::removeInput - Removing " + input + " to the m_ugens list of " + this ); synchronized( m_ugens ) { for ( int i = 0; i < m_ugens.size(); ++i ) { if ( m_ugens.get( i ) == input ) { m_ugens.set( i, null ); } } } } protected void sampleRateChanged() { // ddf: need to let all of the UGens in our list know about the sample rate change synchronized( m_ugens ) { for ( int i = 0; i < m_ugens.size(); i++ ) { UGen u = m_ugens.get( i ); if ( u != null ) { u.setSampleRate( sampleRate() ); } } } } protected void channelCountChanged() { synchronized( m_ugens ) { for( int i = 0; i < m_ugens.size(); ++i ) { UGen u = m_ugens.get( i ); if ( u != null ) { u.setChannelCount( channelCount() ); } } } m_tickBuffer = new float[ channelCount() ]; } @Override protected void uGenerate(float[] channels) { // make sure we are generating the correct number of channels if ( m_tickBuffer == null || m_tickBuffer.length != channels.length ) { m_tickBuffer = new float[channels.length]; // and propagate that to our list synchronized( m_ugens ) { for ( int i = 0; i < m_ugens.size(); ++i ) { UGen u = m_ugens.get( i ); if ( u != null ) { u.setChannelCount( channels.length ); } else // a null entry means it was unpatched, so go ahead and cull now { m_ugens.remove( i ); --i; } } } } // start with silence Arrays.fill( channels, 0 ); synchronized( m_ugens ) { for ( int i = 0; i < m_ugens.size(); ++i ) { // m_tickBuffer should be filled with the correct audio // even if this ugen has generated audio already UGen u = m_ugens.get( i ); if ( u != null ) { u.tick( m_tickBuffer ); processSampleFrame( m_tickBuffer, channels ); } else // a null entry means this ugen was unpatched, so we remove the // entry { m_ugens.remove( i ); --i; } } } } // ddf: I broke this out into its own method so that Sink could extend Summer. // Doing this means not having to rewrite all of the UGen list handling // that Summer already does. The only difference between Summer and Sink // is that Sink produces silence. protected void processSampleFrame(float[] in, float[] out) { for ( int i = 0; i < out.length; ++i ) { out[i] += in[i]; } } /** * Generates a buffer of samples by ticking this UGen mono.length times. * Like the tick method, this will result in all of the */ public void generate(float[] mono) { float[] sample = new float[1]; for ( int i = 0; i < mono.length; i++ ) { tick( sample ); mono[i] = sample[0]; } } public void generate(float[] left, float[] right) { float[] sample = new float[2]; for ( int i = 0; i < left.length; i++ ) { tick( sample ); left[i] = sample[0]; right[i] = sample[1]; } } }