Using expressions in Dilla
As programmers, we are primed to avoid repetition from the day we write our first subroutine. How can we apply the same principles when composing music in the browser with Dilla?
If you are not familiar with Dilla, a small library I made for scheduling notes, you should first have a look at my previous post: "Making a boombap beat with Dilla and the Web Audio API".
A blipping instrument
Since reading text on screen is hardly what makes Web Audio awesome, we'll need a simple instrument to represent our notes. We'll create a simple oscillator, using a waveshape and frequency based on the current value of beat
and tick
.
dilla.on('step', function onStep (step) {
if (step.event !== 'start') return;
// Create oscillator and gain, connect to graph
var oscillator = step.context.createOscillator();
var gainNode = step.context.createGain();
oscillator.connect(gainNode);
gainNode.connect(step.context.destination);
var position = step.args.position.split('.');
var beat = position[1], tick = position[2];
// Set oscillator type based on current beat
var types = ['sine', 'square', 'triangle', 'sawtooth'];
oscillator.type = types[beat - 1];
// Set frequency based on current tick
var freq = 220 + ((440 / 96) * tick);
oscillator.frequency.value = freq;
// Sustain 10ms, then release over 10 ms
gainNode.gain.setValueAtTime(1, step.time);
gainNode.gain.setValueAtTime(1, step.time + 0.1);
gainNode.gain.linearRampToValueAtTime(0, step.time + 0.2);
// Start and stop oscillator
oscillator.start(step.time);
oscillator.stop(step.time + 0.2);
});
Wildcard operator
The asterisk matches any number in a position. Assuming the default 4 beats per bar and 2 bars per loop at 120 bpm, the expression below would generate 8 notes: 1.1.01
, 1.2.01
, 1.3.01
, 1.4.01
, 2.1.01
, 2.2.01
, 2.3.01
and 2.4.01
.
dilla.set('blip', [
['*.*.01']
]);
Odd and even operators
Matches odd or even numbers in a position. If we lower the tempo to 60 bpm, we can hear the frequency going up, and then switching to another waveform on the next beat.
dilla.setTempo(60);
dilla.set('blip', [
['*.*.odd']
]);
Modulus operator
With the modulus operator, you can set repetition in more fine-grained intervals. The expression below would generate 8 notes per beat: 1.1.01
, 1.1.13
, 1.1.25
, 1.1.37
, 1.1.49
, 1.1.61
, 1.1.73
and 1.1.85
, and so on...
dilla.set('blip', [
['*.*.%12']
]);
Combining several expressions is also possible, and Dilla will make sure there are no duplicate notes. You can also define an offset for the modulus operator. The expression below will match ticks 24
, 42
, 60
and 78
for odd beats and 1
, 13
, 25
, 37
, 49
, 61
, 73
and 85
for even beats.
dilla.set('blip', [
['*.odd.24%18'],
['*.even.%12']
]);
Plain javascript
Since we're composing our tune with code, we could also just use plain javascript to add notes. Below is an example of some fun randomness.
function random (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var goal = random(10, 100);
var notes = [];
var position;
while (notes.length < goal) {
position = [random(1, 2), random(1, 4), random(1, 96)].join('.');
notes.push([position]);
}
dilla.set('blip', notes);
Wrapping up
That's just a few examples of how you can use expression operators in Dilla to avoid repetition when composing music in code.
Do you use other strategies for scheduling Web Audio events and creating interesting note patterns? Let's discuss it on Twitter.