/** *

Sequences Overlaid

*

by Benito

*

Pressing (most) keys starts a short sequence.

* *

Playing several sequences at once brings in the bass.

*

Pressing [enter] switches the percussion.

*

Clicking/dragging plays dings:

* */ import net.beadsproject.beads.core.*; import net.beadsproject.beads.data.*; import net.beadsproject.beads.ugens.*; import net.beadsproject.beads.events.*; AudioContext ac; Compressor comp; Envelope clicker, basslev, bassfreq, bd, bdfreq; float bdfreq1 = 70; float bdfreq2 = 40; float targetbassfreq = 110 * 2f/3; ArrayList cue; int lastding = -1; Ding[] dings; ArrayList[] playing; int drumstatus = 1; float[] thescale = {1, 9f/8, 6f/5, 5f/4, 45f/32, 7f/4, 5f/3, 7f/4}; int[] sequence = {11, 13, 14, 12, 8, 9, 12, 13, 4, 7, 5, 6, 3, 7, 3}; void setup() { size(400, 400); cue = new ArrayList(); dings = new Ding[12]; playing = new ArrayList[3]; playing[0] = new ArrayList(); playing[1] = new ArrayList(); playing[2] = new ArrayList(); ac = new AudioContext(256); clicker = new Envelope(ac, 0); bd = new Envelope(ac, 0); bdfreq = new Envelope(ac, bdfreq2); BiquadFilter bdfilt = new BiquadFilter(ac, 1, BiquadFilter.BP_PEAK); bdfilt.setFrequency(bdfreq).setQ(30); bdfilt.addInput(bd); Gain bdgain = new Gain(ac, 1, 20); bdgain.addInput(bdfilt); basslev = new Envelope(ac, 0); bassfreq = new Envelope(ac, 110 * 2f / 3); WavePlayer basstone = new WavePlayer(ac, bassfreq, Buffer.SQUARE); OnePoleFilter bassopf = new OnePoleFilter(ac, 150); bassopf.addInput(basstone); Gain bassgain1 = new Gain(ac, 1, basslev); bassgain1.addInput(bassopf); RandomPWM basspwm = new RandomPWM(ac, RandomPWM.RAMPED_NOISE, 4000, 10000, 1); Function basswarble = new Function(basspwm) { public float calculate() { return x[0] * .25 + .75; } }; Gain bassgain2 = new Gain(ac, 1, basswarble); bassgain2.addInput(bassgain1); comp = new Compressor(ac, 2); comp.setRatio(2); comp.addInput(clicker); Gain mainGain = new Gain(ac, 2, .2); mainGain.addInput(comp); mainGain.addInput(bassgain2); mainGain.addInput(bd); mainGain.addInput(bdgain); ac.out.addInput(mainGain); Bead manager = new Bead() { int cuenum = 0; int clicknum = 0; public void messageReceived(Bead mess) { // if drum status is right, make a click if(drumstatus > 0) { float velocity = .4; switch(clicknum % 12) { case 1: case 3: case 6: case 8: case 10: velocity = .1; break; } clicker.addSegment(velocity, .1); clicker.addSegment(-.3 * velocity, .2); clicker.addSegment(.2 * velocity, .23); clicker.addSegment(-.5 * velocity, .28); clicker.addSegment(.25 * velocity, .31); clicker.addSegment(-.05 * velocity, .4); clicker.addSegment(0, .7); } // bass drum if appropriate if(drumstatus > 1) { switch(clicknum % 24) { case 8: case 10: case 12: case 15: case 19: case 21: bdhit(); break; } } // now to deal with cued sequences //see how many sequences are playing int numseqplaying = playing[0].size() + playing[1].size() + playing[2].size(); // if nothing's playing, make sure next sequence is on the beat if(numseqplaying == 0) { cuenum = 0; } // if a key's been cued, put the appropraite sequence in the appropriate playlist // we'll cap the number of consecutively playing sequences at 10? if(clicknum % 3 == cuenum && numseqplaying <= 20) { if(cue.size() > 0) { int thenum = (Integer)cue.get(0); float theduration = (Float)cue.get(1); cue.remove(0); cue.remove(0); int transpose = 0; float pan = ((float)(thenum * 17) % 43) / 21 - 1; if(thenum >= 'A' && thenum <= 'Z') { transpose += thescale.length; thenum += 'a' - 'A'; } int seqlen = (thenum % 7) + 2; int seqstart = thenum % sequence.length; ArrayList theseq = new ArrayList(); theseq.add(theduration); theseq.add(pan); for(int i = 0; i < seqlen; i++) { theseq.add(sequence[(seqstart + i) % sequence.length] + transpose); } playing[cuenum].add(theseq); cuenum = (cuenum + 2) % 3; } } ArrayList playlist = playing[clicknum % 3]; for(int i = 0; i < playlist.size(); i++) { ArrayList currseq = (ArrayList)playlist.get(i); float thisdur = (Float)currseq.get(0); float thispan = (Float)currseq.get(1); int thisnote = (Integer)currseq.get(2); playNote(thisnote, thisdur * 500, thispan); if(currseq.size() <= 3) { playlist.remove(i); } else { currseq.remove(2); } } // adjust the bass loudness depending on number playing float currlev = basslev.getValue(); float targetlev = numseqplaying - 3; if(numseqplaying < 4) { targetlev = 0; } else { targetlev = (1 + 3 * targetlev / (targetlev + 4)) * .1; } if(targetlev > currlev) { basslev.clear(); basslev.addSegment(targetlev, 750); basslev.addSegment(0, 6000); } //check the dings - remove done dings for(int i = 0; i < dings.length; i++) { Ding ding = dings[i]; if(ding != null) { if(ding.env.getValue() == 0) { dings[i] = null; } } } clicknum++; } }; Clock clock = new Clock(ac, 200); clock.setTicksPerBeat(1); clock.addMessageListener(manager); ac.out.addDependent(clock); ac.start(); } void playNote(int note, float duration, float pan) { float freq = 108f * pow(2, note / thescale.length) * thescale[note % thescale.length] + 2; WavePlayer saw = new WavePlayer(ac, freq, Buffer.SINE); Envelope env = new Envelope(ac, 0); Gain g = new Gain(ac, 1, env); g.addInput(saw); Panner p = new Panner(ac, pan); p.addInput(g); env.addSegment(.8, 15); env.addSegment(.4, 40); env.addSegment(0, max(duration - 70, 30)); env.addSegment(0, 30, new KillTrigger(p)); comp.addInput(p); } void bdhit() { bd.clear(); bd.addSegment(.8, 4); bd.addSegment(0, 6); bdfreq.clear(); bdfreq.addSegment(bdfreq1, 4); bdfreq.addSegment(bdfreq2, 40); } void doDing(int dingnum) { float pan = (float)mouseX / width * 2 - 1; if(dings[dingnum] != null && dings[dingnum].g.isDeleted() == false) { dings[dingnum].ding(pan); } else { float freq = 442f * 3f/2 * thescale[(dingnum + 5) % thescale.length] * pow(2, (dingnum + 5) / thescale.length) - 3; dings[dingnum] = new Ding(freq, pan); } } int generateDingNum() { return (((int)((float)mouseY / height * dings.length * .999)) * 7) % dings.length; } class Ding { Envelope env; Gain g; Panner p; Envelope panenv; public Ding(float freq, float pan) { println(freq); env = new Envelope(ac, .0001); WavePlayer tone = new WavePlayer(ac, freq, Buffer.SINE); g = new Gain(ac, 1, env); g.addInput(tone); panenv = new Envelope(ac, pan); p = new Panner(ac, panenv); p.addInput(g); comp.addInput(p); ding(pan); } public void ding(float pan) { env.clear(); env.addSegment(.3, 10); env.addSegment(0, 10000, .1, null); env.addSegment(0, 100, new KillTrigger(p)); panenv.clear(); panenv.addSegment(pan, 50); } } void draw() { background(0); // this is all you! } void keyTyped() { if(key == ENTER) { drumstatus = (drumstatus + 1) % 3; } else { if(cue.size() < 10) { cue.add((int)key); cue.add((float)mouseX / width); } bassfreq.clear(); bassfreq.addSegment(targetbassfreq, 300); int bassnote = (key % 17 + 4); targetbassfreq = 55 * 2f/3 * thescale[bassnote % thescale.length] * pow(2, bassnote / thescale.length); bassfreq.addSegment(targetbassfreq, 500); } } void keyPressed() { // don't let hitting shut down the sketch if(key == ESC) key = 0; } void mousePressed() { int dingnum = generateDingNum(); doDing(dingnum); lastding = dingnum; } void mouseDragged() { int dingnum = generateDingNum(); if(dingnum != lastding) { doDing(dingnum); lastding = dingnum; } }