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.