/** * 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(); }