/**
* This "glitch" generator uses the percentage values in the sliders to determine whether or not to place a glitch 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
* The "V" sliders control triggering of glitches in the vocal track and the "G" sliders control triggering of glitches in the drum track.
*
* Tempo: the tempo of the generated sequence.
* Measures: how many measures to generate.
* AddKick: if lit, a kick drum sound will be added on every quarter note.
* MuteVox: if lit, the sequence will be generated without vocals.
* FadeGlitch: if lit, each glitch note in the drum track will be faded in over its duration.
* Randomize: picks random percentages for all sliders.
* Generate: generates a sequence using the current settings.
* Perc Glitch: a range used to determine how short each glitch loop will be for the drums.
* Vox Glitch: a range used to determine how short each glitch loop will be for the vocals.
* The higher glitch values will give you "buzzier" audio and lower values will sound more like a skipping record.
*
* The playback rate of both tracks are scaled based on the tempo you select.
* Lower tempos will result in lower-pitched audio and vice-versa.
* If you want to hear the entire vocal track you should generate around 104 measures.
*
* Vocals: Half Life by Imogen Heap
* Drums: Sample from Hydra Remix By Koen Groeneveld
*
**/
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;
float hydraTempo = 128.f;
FilePlayer hydra;
TickRate hydraRate;
float heapTempo = 109.85f;
FilePlayer heap;
TickRate heapRate;
Multiplier heapAmp;
SampleRepeat heapGlitch;
Multiplier loopAmp;
Line loopAmpSweep;
public float Tempo = 128.f;
public int Measures = 16;
public boolean AddKick = true;
public boolean FadeGlitch = true;
public boolean MuteVox = false;
ControlP5 gui;
Range glitchRange;
Range voxGlitchRange;
float[] glitchProb = 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
};
float[] voxProb = new float[] { 0.1f, 0.2f, 0.05f, 0.1f,
0.3f, 0.1f, 0.1f, 0.03f,
0.02f, 0.12f, 0.09f, 0.32f,
0.06f, 0.25f, 0.03f, 0.01f
};
void Randomize()
{
for(int i = 0; i < glitchProb.length; ++i)
{
glitchProb[i] = random( 0.f, 1.f );
gui.controller("G" + (i+1)).setValue( glitchProb[i] );
voxProb[i] = random( 0.f, 1.f );
gui.controller("V" + (i+1)).setValue( voxProb[i] );
}
}
public void controlEvent( ControlEvent theEvent )
{
int index = theEvent.controller().id();
if ( theEvent.controller().name().startsWith("G") )
{
glitchProb[ index ] = theEvent.controller().value();
}
else if ( theEvent.controller().name().startsWith("V") )
{
voxProb[ index ] = theEvent.controller().value();
}
}
void setup()
{
size( 800, 500 );
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 < glitchProb.length; ++i )
{
if ( i % 4 == 0 ) xpos += 20;
gui.addSlider( "V" + (i + 1), 0.f, 1.f, voxProb[i], xpos, sliderStartY, sliderWidth, sliderHeight ).setId( i );
gui.addSlider( "G" + (i + 1), 0.f, 1.f, glitchProb[i], xpos, sliderStartY + sliderHeight + 40, sliderWidth, sliderHeight ).setId( i );
xpos += sliderSpacing;
}
gui.addNumberbox( "Tempo", Tempo, 40, 220, 40, 15 );
gui.addNumberbox( "Measures", Measures, 100, 220, 20, 15 );
gui.addToggle("AddKick", AddKick, 160, 220, 15, 15 );
gui.addToggle("MuteVox", MuteVox, 220, 220, 15, 15 );
gui.addToggle("FadeGlitch", FadeGlitch, 280, 220, 15, 15 );
gui.addBang("Randomize", 360, 220, 15, 15 );
gui.addBang( "Generate", 420, 220, 15, 15 );
glitchRange = gui.addRange("Perc Glitch", 0.f, 6.2f, 1.f, 4.f, 40, 260, 100, 15);
glitchRange.setDecimalPrecision(0);
glitchRange.setBroadcast(false);
voxGlitchRange = gui.addRange("Vox Glitch", 0.f, 6.2f, 1.1f, 4.f, 250, 260, 100, 15 );
voxGlitchRange.setDecimalPrecision(0);
voxGlitchRange.setBroadcast(false);
minim = new Minim(this);
mainOut = minim.getLineOut();
// setup the main loop that we will glitch
hydra = new FilePlayer( minim.loadFileStream( "hydra_loop.aif", 1024, true ) );
hydraRate = new TickRate( 1.f );
hydraRate.setInterpolation( true );
loopAmp = new Multiplier( 0.8f );
hydra.patch( hydraRate ).patch( loopAmp );
loopAmpSweep = new Line( 0.f, 0.001f, 0.8f );
loopAmpSweep.patch( loopAmp.amplitude );
// setup the vox we'll layer on top
heap = new FilePlayer( minim.loadFileStream( "imogen_heap_half_life_vox.mp3", 1024, false ) );
heapRate = new TickRate( 1.f );
heapRate.setInterpolation( true );
heapGlitch = new SampleRepeat( heapTempo, 0.25f );
heapAmp = new Multiplier( 0.8f );
heap.patch( heapAmp ).patch( heapGlitch ).patch( heapRate );
}
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, 128 );
gui.controller("Measures").setValue( Measures );
hydra.rewind();
hydra.loop();
hydraRate.value.setLastValue( Tempo / hydraTempo );
heap.rewind();
heap.play();
heapRate.value.setLastValue( Tempo / heapTempo );
float lowGlitch = glitchRange.lowValue();
float hiGlitch = glitchRange.highValue();
float lowVoxGlitch = voxGlitchRange.lowValue();
float hiVoxGlitch = voxGlitchRange.highValue();
if ( FadeGlitch )
{
if ( !loopAmp.amplitude.isPatched() )
{
loopAmpSweep.patch( loopAmp.amplitude );
}
}
else
{
if ( loopAmp.amplitude.isPatched() )
{
loopAmpSweep.unpatch( loopAmp );
}
loopAmp.amplitude.setLastValue( 1.f );
}
mainOut.pauseNotes();
mainOut.setNoteOffset( 0.f );
SampleCue lastSample = null;
float lastNoteTime = 0.f;
float measureBegin = 0.f;
mainOut.playNote( 0.f, 4.f * Measures, new SequenceBegin() );
for( int m = 0; m < Measures; ++m )
{
for( int t = 0 ; t < glitchProb.length; ++t )
{
float noteTime = measureBegin + t * 0.25f;
if ( prob( glitchProb[t] ) )
{
// actually cue up the last note because we know how long it needs to be now
if ( lastSample != null )
{
float duration = noteTime - lastNoteTime;
//println("Cueing note of duration " + duration + " at time " + lastNoteTime + ". Next note will be at " + noteTime);
mainOut.playNote( lastNoteTime, duration, lastSample );
}
int loopStart = 117 * t;
float slice = pow( 2.f, int( random( lowGlitch, hiGlitch ) ) );
int loopEnd = loopStart + int( 468.f / slice );
lastNoteTime = noteTime;
lastSample = new SampleCue( loopStart, loopEnd );
}
if ( !MuteVox && prob( voxProb[t] ) )
{
float fglitch = random( lowVoxGlitch, hiVoxGlitch );
int iglitch = int( fglitch );
float glitchLen = 0.5f / pow( 2.f, iglitch );
println("Cueing vox note with fglitch " + fglitch + ", iglitch " + iglitch + ", glitchLen " + glitchLen);
vox( noteTime, 1.f, glitchLen );
}
}
if ( AddKick )
{
kick( measureBegin, 0.25f, 0.9f );
kick( measureBegin + 1.f, 0.25f, 0.9f );
kick( measureBegin + 2.f, 0.25f, 0.9f );
kick( measureBegin + 3.f, 0.25f, 0.9f );
}
measureBegin += 4;
}
if ( lastSample != null )
{
mainOut.playNote( lastNoteTime, measureBegin - lastNoteTime, lastSample );
}
mainOut.playNote( measureBegin, 0.f, new SequenceEnd() );
mainOut.resumeNotes();
}
//////////////////////////////////////////////////
//
// 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()
{
//println("Current loopAmp is " + loopAmp.amplitude.getLastValue());
// 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();
}