RSS Feed

Lisp Project of the Day

cl-collider

You can support this project by donating at:

Donate using PatreonDonate using Liberapay

cl-collidersound

Documentation🥺
Docstrings🥺
Tests 🥺
Examples😀
RepositoryActivity😀
CI 🥺

This library is an interface to a very interesting algorithmic audio synthesizer - SuperCollider. SuperCollider itself has a builtin programming language, but cl-collider makes it possible to write music in lisp interactively.

Here is how a simple program in sclang looks like:

// 60Hz Gabber Rave 1995
Server.default.boot;

(
SynthDef(\gabberkick, {
    var snd, freq, high, lfo;
    freq = \freq.kr(440) * (Env.perc(0.001, 0.08, curve: -1).ar * 48 * \bend.kr(1)).midiratio;
    snd = Saw.ar(freq);
    snd = (snd * 100).tanh + ((snd.sign - snd) * -8.dbamp);
    high = HPF.ar(snd, 300);
    lfo = SinOsc.ar(8, [0, 0.5pi]).range(0, 0.01);
    high = high.dup(2) + (DelayC.ar(high, 0.01, lfo) * -2.dbamp);
    snd = LPF.ar(snd, 100).dup(2) + high;
    snd = RLPF.ar(snd, 7000, 2);
    snd = BPeakEQ.ar(snd, \ffreq.kr(3000) * XLine.kr(1, 0.8, 0.3), 0.5, 15);
    snd = snd * Env.asr(0.001, 1, 0.05).ar(2, \gate.kr(1));
    Out.ar(\out.kr(0), snd * \amp.kr(0.1));
}).add;

SynthDef(\hoover, {
    var snd, freq, bw, delay, decay;
    freq = \freq.kr(440);
    freq = freq * Env([-5, 6, 0], [0.1, 1.7], [\lin, -4]).kr.midiratio;
    bw = 1.035;
    snd = { DelayN.ar(Saw.ar(freq * ExpRand(bw, 1 / bw)) + Saw.ar(freq * 0.5 * ExpRand(bw, 1 / bw)), 0.01, Rand(0, 0.01)) }.dup(20);
    snd = (Splay.ar(snd) * 3).atan;
    snd = snd * Env.asr(0.01, 1.0, 1.0).kr(0, \gate.kr(1));
    snd = FreeVerb2.ar(snd[0], snd[1], 0.3, 0.9);
    snd = snd * Env.asr(0, 1.0, 4, 6).kr(2, \gate.kr(1));
    Out.ar(\out.kr(0), snd * \amp.kr(0.1));
}).add;
)

(
var durations;
durations = [1, 1, 1, 1, 3/4, 1/4, 1/2, 3/4, 1/4, 1/2];
Ppar([
    Pbind(*[
        instrument: \gabberkick,
        amp: -23.dbamp,
        freq: 60,
        legato: 0.8,
        ffreq: Pseq((0..(durations.size * 4 - 1)).normalize, inf).linexp(0, 1, 100, 4000),
        dur: Pseq(durations, inf),
        bend: Pfuncn({ |x| if(x < (1/2), 0.4, 1) }, inf) <> Pkey(\dur),
    ]),
    Pbind(*[
        instrument: \hoover,
        amp: -20.dbamp,
        midinote: 74,
        dur: durations.sum * 2,
        sustain: 7,
    ])
]).play(TempoClock(210 / 60));
)

I wasn't able to translate it into the Lisp form, because cl-collider does not have documentation and diving into it will require too much time. However, there is a working code example from its README, which you can try in the REPL:

POFTHEDAY> (named-readtables:in-readtable :sc)
POFTHEDAY> (setf sc:*s* (sc:make-external-server
                         "localhost" :port 48800))
POFTHEDAY> (use-package :sc)

POFTHEDAY> (defsynth saw-synth ((note 60) (dur 4.0))
             (let* ((env (env-gen.kr (env [0 .2 0]
                                          [(* dur .2) (* dur .8)])
                                     :act :free))
                    (freq (midicps note))
                    (sig (lpf.ar (saw.ar freq env)
                                 (* freq 2))))
               (out.ar 0 [sig sig])))

POFTHEDAY> (defun make-melody (time n &optional (offset 0))
             (when (> n 0)
               (at time (synth 'saw-synth
                               :note (+ offset (alexandria:random-elt
                                                '(62 65 69 72)))))
               (let ((next-time (+ time (alexandria:random-elt
                                         '(0 1 2 1.5)))))
                 (callback next-time #'make-melody
                           next-time (- n 1) offset))))

POFTHEDAY> (make-melody (quant 4) 16)
POFTHEDAY> (make-melody (+ 4 (quant 4)) 16 12)

;; This will stop the music
POFTHEDAY> (sc:stop)

Here is a demo, showing how does live coding works with cl-collider:

https://www.youtube.com/watch?v=pZyuHjztARY

To try cl-collider, you'll need to install a SuperCollider. On OSX it is as simple as doing:

brew cask install supercollider

Brought to you by 40Ants under Creative Commons License