Sequencer Gadgets
While Kronos is usually geared towards building instruments and effects, nothing stops us from using its generative powers for musical phrases as well. The most straightforward way to do so invokes they way early step sequencers used to work: we simply need a circuit that will cycle through a set of values at a specified tempo.
Rather than build such sequencers ourselves, we will make use of the kronoslang/gadgets package like so:
; from https://github.com/kronoslang/gadgets
Import [kronoslang/gadgets 0.1.2]
; Bring functions from Gadgets: into the global namespace.
; Pattern-Seq, Semitone->Freq, Artikulator, Arpeggiator and Arpeggiator<->
Use Gadgets
Let's have a look at what's inside. All the sequencers are built to stay in sync with each other by utilizing stateless tempo mapping in double precision. They tick at a control rate of one in 64 audio samples.
Pattern-Seq
Pattern-Seq
is an all-round sequencer gadget that cycles through a list of identically typed elements at a specified rate.
Import Gen
note = Pattern-Seq(125 0 4 2 5 4 7 5 9 7 11 9 12 11 14)
freq = Semitone->Freq(220 note)
snd = Gen:Tri(freq) * 0.2
Pattern-Seq
supports arbitrary values and tuples if that strikes your fancy. Here we sequence the last two parameters of a resonator filter by one tuple-valued sequence.
Import Filter
freq-bw = [(440 40)
(550 300)
(330 200)
(110 100)
(660 30)
(880 10)
(550 30)]
filter-params = Pattern-Seq(350 freq-bw)
pwm = Gen:Pulse(55 Gen:Phasor(0.2))
snd = Filter:Resonator(pwm filter-params) * 0.3
If that left you scratching your head, the following example might make it clearer via explicit destructuring of the sequencer output:
Import Control
; note width filter
seq = [(0 0.1 300)
(3 0.3 800)
(7 0.4 200)
(10 0.5 100)
(12 0.6 350)
(10 0.2 800)
(7 0.8 400)
(3 0.7 660)]
; the sequencer outputs 3-tuples, bind each
; element to a named symbol via an interpolator
(note width filter) =
Control:Smooth(Pattern-Seq(250 seq) 40)
freq = Semitone->Freq(220 note)
pwm = Gen:Pulse(freq width)
snd = Filter:Resonator(pwm filter filter * 0.1)
Random-Seq
Sometimes we just want to make a mess. Random-Seq
provides tempo-synced random values between a lower and a higher bound, with a seed value that determines the exact sequence.
freq = Random-Seq(250 1 20 2000)
snd = Gen:Sin(Control:Smooth(freq 100)) * 0.2
Combined with a pattern sequencer we can create a balance of musical chaos and order.
tempo = 108 * 4
note = Pattern-Seq(tempo 0 0 2 3)
freq = Semitone->Freq(55 note)
pwm = Gen:Pulse(freq Gen:Phasor(0.1))
filt = Random-Seq(tempo 2 80 800)
snd = Filter:Resonator(pwm filt 300) * 0.3
Artikulator
Artikulator is kind of like a variable-width pulse oscillator driven by a sequencer. It is intended to be used with envelope generators. As is, it can make rectangular envelopes. For each step of the sequence, Artikulator
will output 1
and then 0
. The timings for these are determined by the value at each step: for 0.1
, the gate will be open for 10% of the sequencer step, and for 0.5
it will close halfway through the step.
rect = Artikulator(120 0.1 0.9 0.3 0.5)
snd = Wave:Tri(220) * rect * 0.2
Combined with an envelope generator, we can make rhythms and articulations.
Import Envelope
tempo = 120
gate = Artikulator(
tempo * 4
0.5 0 0.5 0 1 0.5 0.5 0.5 1 0 0 0)
env = Envelope:ADSR(gate 0.03 0.3 0.5 0.1)
noise = Gen:Noise() * env
filt = Pattern-Seq(tempo 300 600)
snd = Filter:Resonator(noise filt filt * 0.5)
Arpeggiators
No discussion of sequencing classics would be complete without the arpeggiator. It is a slight specialization of the pattern sequencer, specifically for note numbers, that can produce additional octave transpositions automatically.
tempo = 136 * 4
octaves = 4
note = Arpeggiator(tempo octaves
0 3 7 10 3 7 10 14)
freq = Control:Smooth(Semitone->Freq(55 note) 50)
snd = Wave:Tri(freq) * 0.2
Often we want to go back and forth, which involves ping-ponging the octave transposition and rotating the note pattern so that the "corner" where we turn back down makes sense. Arpeggiator<->
does that for you.
note = Arpeggiator<->(tempo octaves
0 3 7 10)
; see above for definition of freq
snd = Wave:Tri(freq) * 0.2
Putting it Together
More the merrier! Let's use all the gadgets together to introduce some musical variation and phrasing to an arpeggio.
tempo = 128 * 4
note = Arpeggiator(tempo octaves
0 3 7 10 3 7 10 14)
width = Control:Smooth(
Random-Seq(tempo 1 0.01 0.99)
15)
pwm = Gen:Pulse(freq width)
; slur-short-short articulation
gate = Artikulator(tempo 1 0.5 0.5)
env = Envelope:ADSR(gate 0.02 0.3 0.5 0.05)
; random mod depth for env->filter
amt = Random-Seq(tempo 3 500 5000)
; parabolic shape
env2 = env * env
; random resonance
rez = Random-Seq(tempo 2 0 0.05)
arp = Filter:Lowpass(pwm 60 + env2 * amt rez) * env2 * 0.2
snd = arp
Let's add some pads!
chords = [(0 5 7)
(3 5 10)
(5 7 12)
(3 5 10)]
oscs = Algorithm:Map(
note => Gen:Saw(Semitone->Freq(220 note))
Pattern-Seq(tempo / 8 chords))
pad-gate =
Artikulator(tempo
0 0 1 0
0 0.3 0 0.2)
pad-env =
Envelope:ADSR(pad-gate 0.01 0.2 0.3 0.03)
pads = Filter:Lowpass(
Average(oscs) * 0.4
20 + 2000 * pad-env
0.7)
snd = pads
snd = pads + arp
Visual Version
This article also demonstrates how to mix visual and textual code. How so? Well, kronoslang/gadgets
is implemented in Veneer. You can open it up in patch form, or take a look at the code Veneer generates and command line Kronos pulls in. There's also a generative music patch similar to this textual example.