/**
* This drum beat generator uses the percentage values in the sliders
* to determine whether or not to place a note in the sequence as it generates it.
* You can force a note to always be present at a particular step by setting the slider to 1.0
* You can force a note to never be present at a particular step by setting the slider to 0.0
*
* Tempo: the tempo of the generated sequence.
* Measures: how many measures to generate.
* Exclusive: if lit, kick and snare will never occur simultaneously.
* Randomize: picks random percentages for all sliders.
* Generate: generates a sequence using the current settings.
*
* The playback rate of the chime sample played over the generated beat is scaled
* based on the tempo you select. Lower tempos will result in lower-pitched chimes
* and vice-versa.
*
**/
import controlP5.*;
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
import ddf.minim.ugens.*;
Minim minim;
AudioOutput mainOut;
FilePlayer chimes;
TickRate chimesRate;
public float Tempo;
public int Measures;
public boolean Exclusive;
ControlP5 gui;
float[] kickProb = new float[] { 0.9f, 0.1f, 0.4f, 0.2f,
0.5f, 0.3f, 0.4f, 0.2f,
0.8f, 0.05f, 0.2f, 0.6f,
0.75f, 0.1f, 0.5f, 0.2f
};
float[] snareProb = new float[] { 0.f, 0.f, 0.f, 0.5f,
0.9f, 0.1f, 0.f, 0.3f,
0.2f, 0.f, 0.05f, 0.4f,
0.7f, 0.1f, 0.2f, 0.f
};
float[] hatProb = new float[] { 0.6f, 0.2f, 0.9f, 0.2f,
0.6f, 0.2f, 0.9f, 0.2f,
0.6f, 0.2f, 0.9f, 0.2f,
0.6f, 0.2f, 0.9f, 0.2f,
};
void Randomize()
{
for(int i = 0; i < kickProb.length; ++i)
{
kickProb[i] = random( 0.f, 1.f );
gui.controller("K" + (i+1)).setValue( kickProb[i] );
snareProb[i] = random( 0.f, 1.f );
gui.controller("S" + (i+1)).setValue( snareProb[i] );
hatProb[i] = random( 0.f, 1.f );
gui.controller("H" + (i+1)).setValue( hatProb[i] );
}
}
public void controlEvent( ControlEvent theEvent )
{
int index = theEvent.controller().id();
if ( theEvent.controller().name().startsWith("K") )
{
kickProb[ index ] = theEvent.controller().value();
}
else if ( theEvent.controller().name().startsWith("S") )
{
snareProb[ index ] = theEvent.controller().value();
}
else if ( theEvent.controller().name().startsWith("H") )
{
hatProb[ index ] = theEvent.controller().value();
}
}
void setup()
{
size( 800, 600 );
colorMode(HSB, 360, 1, 1);
gui = new ControlP5(this);
int sliderHeight = 50;
int sliderWidth = 10;
int sliderSpacing = 40;
int sliderStartY = 300;
int xpos = 20;
for(int i = 0; i < kickProb.length; ++i )
{
if ( i % 4 == 0 ) xpos += 20;
gui.addSlider( "K" + (i + 1), 0.f, 1.f, kickProb[i], xpos, sliderStartY + sliderHeight * 2 + 40, sliderWidth, sliderHeight ).setId( i );
gui.addSlider( "S" + (i+1), 0.f, 1.f, snareProb[i], xpos, sliderStartY + sliderHeight + 20, sliderWidth, sliderHeight ).setId( i );
gui.addSlider( "H" + (i+1), 0.f, 1.f, hatProb[i], xpos, sliderStartY, sliderWidth, sliderHeight ).setId( i );
xpos += sliderSpacing;
}
Tempo = 130.f;
gui.addNumberbox( "Tempo", 130.f, 40, 220, 40, 15 );
Measures = 16;
gui.addNumberbox( "Measures", 16, 100, 220, 20, 15 );
gui.addToggle("Exclusive", false, 160, 220, 15, 15 );
gui.addBang("Randomize", 220, 220, 15, 15 );
gui.addBang( "Generate", 280, 220, 15, 15 );
minim = new Minim(this);
mainOut = minim.getLineOut();
chimes = new FilePlayer( minim.loadFileStream( "chimes.wav", 1024, true ) );
chimesRate = new TickRate( 1.f );
chimesRate.setInterpolation( true );
Multiplier amp = new Multiplier( 0.2f );
chimes.patch( amp ).patch( chimesRate );
}
boolean prob( float pct )
{
return random(1.f) < pct;
}
public void Generate()
{
gui.controller("Generate").hide();
Tempo = constrain( Tempo, 20.f, 480.f );
gui.controller("Tempo").setValue( Tempo );
mainOut.setTempo( Tempo );
Measures = (int)constrain( Measures, 1, 64 );
gui.controller("Measures").setValue( Measures );
chimesRate.value.setLastValue( Tempo / 80.f );
mainOut.pauseNotes();
for( int i = 0; i < Measures; ++i )
{
mainOut.setNoteOffset( i * 4 );
drumLoop();
if ( i % 8 == 0 )
{
mainOut.playNote( 0.f, 4.f * 8.f, new Chimes() );
}
}
mainOut.setNoteOffset( Measures * 4 );
kick( 0.f, 2.f, 0.5f );
mainOut.playNote( 2.f, 0.f, new SequenceEnd() );
mainOut.resumeNotes();
}
void drumLoop()
{
// tracks whether there was a snare or kick on the last step
boolean didSnare = false;
boolean didKick = false;
for( int i = 0; i < kickProb.length; ++i )
{
if ( prob( hatProb[i] ) )
{
float amp = random( 0.05, 0.3f );
hat( i * 0.25f, amp );
}
boolean doSnare = prob( snareProb[i] );
if ( doSnare )
{
float amp = 1.f;
if ( didSnare ) amp = 0.2f;
if ( didKick ) amp = 2.f;
snare( i * 0.25f, amp );
didSnare = true;
}
else
{
didSnare = false;
}
if ( Exclusive && doSnare )
{
didKick = false;
}
else if ( prob( kickProb[i] ) )
{
float amp = 1.f;
if ( didKick ) amp = 0.2f;
if ( Exclusive && didSnare ) amp = 2.f;
kick( i * 0.25f, 0.25f, amp );
didKick = true;
}
else
{
didKick = false;
}
}
}
class Chimes implements Instrument
{
void noteOn( float dur )
{
chimes.play( 0 );
chimesRate.patch( mainOut );
}
void noteOff()
{
chimesRate.unpatch( mainOut );
}
}
class SequenceEnd implements Instrument
{
void noteOn( float dur )
{
gui.controller("Generate").show();
gui.controller("Generate").setMousePressed( false );
}
void noteOff()
{
}
}
//////////////////////////////////////////////////
//
// DRAWING DOWN HERE
//
float hueForSample( float sample )
{
return map( abs(sample), 0, 1, 120, 450 );
}
float waveSize = 50.f;
float waveCenter = 100.f;
// draw is run many times
void draw()
{
// erase the window to black
background( 0 );
// draw using a white stroke
stroke( 255 );
// draw the waveforms
for( int i = 0; i < mainOut.bufferSize() - 1; i++ )
{
// find the x position of each buffer value
float x1 = map( i, 0, mainOut.bufferSize(), 0, width );
float x2 = map( i+1, 0, mainOut.bufferSize(), 0, width );
// draw a line from one buffer position to the next for both channels
stroke( hueForSample(mainOut.left.get(i)), 1, 1 );
line( x1, waveCenter - waveSize + mainOut.left.get(i)*waveSize, x2, waveCenter - waveSize + mainOut.left.get(i+1)*waveSize);
stroke( hueForSample(mainOut.right.get(i)), 1, 1 );
line( x1, waveCenter + waveSize + mainOut.right.get(i)*waveSize, x2, waveCenter + waveSize + mainOut.right.get(i+1)*waveSize);
}
}
// stop is run when the user presses stop
void stop()
{
// close the AudioOutput
mainOut.close();
// stop the minim object
minim.stop();
super.stop();
}