LFE Music Programming with undertone
by Duncan McGreggor
Published by Cowboys 'N' Beans Books
https://github.com/cnbbooks ◈ http://cnbb.pub/ ◈ info@cnbb.pub
First electronic edition published: 2020
© 2020, Duncan McGreggor
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License
Cover art: Book covers for the 1.x series of LFE vertsions were inspired by the 1968 DEC FOCAL Programming Manual with stylised PDP-8 as the dominant cover graphic. For the LFE 2.x series, a new cover was designed along the same lines but using a PDP-11 as the basis.
Introduction
TBD
LFE and Erlang
Music Programming
MIDI
Audio Processing
Open Sound Control
Extempore
The undertone REPL
Programming Basics
The undertone Architecture
Quick Start
To try out undertone
, you can just clone the project itself:
$ git clone git@github.com:lfex/undertone.git
$ cd undertone
and then start up the REPL:
$ rebar3 repl
Note that this does require that you have a modern Erlang installed (version 21 or above) as well as rebar3
, the famous build and dependency management tool in the BEAM ecosystem.
Extempore
Tested against extempore
version 0.8.7
.
Download the latest Extempore from the project releases page on Github.
Assuming you've started extempore
and have the LFE REPL running,
as previously described, you will have been automatically connected to the
Extempore TCP server. As such, you're ready to start living coding!
There are several ways to do this from LFE:
- From an LFE-managed REPL supporting native Extempore syntax,
- From an LFE-managed REPL with hybrid Extempore and LFE support, and
- From LFE itself (limited Extempore support)
The Extempore REPL
At the LFE prompt, start up the Extempore REPL:
lfe> (undertone.repl.extempore:start)
extempore>
Once at the extempore>
prompt, you will be able to enter native Extempore
expressions (Scheme and xtlang). These will be passed to the Extempore TCP
server as-is (executed asynchronously and non-blocking).
Additionally, you may call other supported functions. To see the list of
supported REPL function, type (help)
:
extempore> (help)
The Extempore undertone REPL
Built-in functions:
(call body) -- make an explicitly blocking call to Extempore, with 'body' being
a valid Extempore expression
(check-xt) -- check on the status of Extempore (no response indicates a health
problem, possibly requiring a restart of the 'extempore' binary)
(eom) -- alias for '(term)'
(exit) -- alias for '(quit)'
(h) -- alias for '(help)'
(help) -- display this information
(quit) -- quit the Extempore REPL and return to the LFE REPL
(run file) -- load the code in the given file and run in Extempore
(term) -- send message-terminating character sequence to force Extempore
end-of-message (useful when troubleshooting)
(version) -- display all the version info for undertone
Extempore support:
All S-expressions other than the ones listed above will be treated as Extempore
Scheme / xtlang code and sent to the Extempore TCP server (compiler service) as
an asynchronous call (no result, no output printed). If you would like to block
on particular calls and see their return values, be sure to use the '(call ...)'
REPL function.
Most (if not all) of the official Extempore examples should work without modification in this REPL.
The Undertone REPL
TBD
Native LFE
Note that only a limited number of Exempore forms are supported from within LFE right now.
Load the appropriate Extempore files into the server and the LFE macros into the current REPL session:
(xt:sys-load "examples/sharedsystem/setup.xtm")
(include-lib "undertone/include/xt-patterns.lfe")
Play an ascending scale using the second synthesizer that comes with Extempore:
(/> 'ascending-scale 4 0 (play 'syn2 '@1 80 'dur) (scale 4 8))
Note that the Extempore syntax for this is :>
, a character combination not
supported in LFE symbols, so />
was settled upon instead. Similarly, below you
will see //
used in LFE as an analog for the :|
used in Extempore.
Then change the tempo:
(set-tempo! 72)
You can stop the synth in two ways -- changing 'play' form to the 'stop' form while keeping the remaining body the same (this is useful for live coding scenarios):
(// 'ascending-scale 4 0 (play 'syn2 '@1 80 'dur) (scale 4 8))
or by calling the 'stop' form using just the pattern name you defined in the 'play' form:
(// 'ascending-scale)
OSC
The Erlang Server
Tested against erlsci/osc
2.0
and 2.1
.
lfe> (set c (undertone.osc.client:connect "localhost" 2357))
; ok#(client #Pid<0.276.0> #Pid<0.277.0>)
lfe> (undertone.osc.client:echo c)
; ok
If you view the output of the running Erlang OSC server, you should see some debug output logged:
=INFO REPORT==== 27-Nov-2020::16:14:05.986773 ===
Received message: []
You can also check passed arguements:
lfe> (undertone.osc.client:echo c '(a list of args))
ok
=INFO REPORT==== 27-Nov-2020::16:35:28.652299 ===
Received message: [a,list,'of',args]
SuperCollider ↟
Tested against SuperCollider 3.11.2
.
Connecting
Start up the SuperCollider GUI / IDE, then in the editor enter the following:
s.options.maxLogins = 8;
s.reboot;
Server.default.options.asOptionsString
The last line will help you confirm the current settings, showing what they
would be if you'd started scsynth
manually. In particular, you want to confirm
that you see -l 8
.
Then, in a terminal at the LFE REPL:
lfe> (set c (sc.client:connect "localhost" 57110))
;#(client #Pid<0.344.0> #Pid<0.345.0>)
Once the client is set up, do a quick check to see that you are connected to the right server / version and that there are synthesizer definitions loaded:
lfe> (sc.client:version c)
;(#(version "3.11.2") #(branch "HEAD") #(commit-id "9a34118e"))
lfe> (sc.client:status c)
;(#(unit-generators 0)
; #(synths 0)
; #(gruops 9)
; #(loaded-synth-definitions 106)
; #(cpu-average-usage 0.030904095619916916)
; #(cpu-peak-usage 0.2695612609386444)
; #(nominal-sample-rate 4.41e4)
; #(actual-sample-rate 44099.98856935304))
Playing Sounds
First create a handful of instances of the default synth and then stop them, until we're ready:
lfe> (set synth-ids '(1000 1001 1002 1003 1004))
;"ϨϩϪϫϬ"
lfe> (set (list s0 s1 s2 s3 s4) (list-comp
((<- id synth-ids))
(sc.client:create-synth c id)))
;"ϨϩϪϫϬ"
lfe> (list-comp ((<- id synth-ids)) (sc.client:stop-node c id))
;(ok ok ok ok ok)
Instead of a boring C chord, let's take some notes from the C scale's Locrian
mode using B
, E
, F
, A
, and B
(a combination rarely heard!):
lfe> (include-lib "include/notes.lfe")
;|-- loaded include: notes --|
lfe> (sc.client:set-node c s0 `("freq" ,(B3) out 0))
;ok
lfe> (sc.client:set-node c s1 `("freq" ,(E3) out 1))
;ok
lfe> (sc.client:set-node c s2 `("freq" ,(F3) out 0))
;ok
lfe> (sc.client:set-node c s3 `("freq" ,(A3) out 0))
;ok
lfe> (sc.client:set-node c s4 `("freq" ,(B4) out 1))
;ok
Now play them all:
lfe> (list-comp ((<- id synth-ids)) (sc.client:start-node c id))
;(ok ok ok ok ok)
For the MIDI-minded, in addition to the notes.lfe
include, there is a
midi-notes.lfe
file with functions defined for getting notes by MIDI number.
And then, when you're done listening to that beautiful dissonance:
lfe> (list-comp ((<- id synth-ids)) (sc.client:stop-node c id))
;(ok ok ok ok ok)
Ardour ↟
Tested with versions 5 and 6 of Ardour.
lfe> (set c (ardour.client:connect "localhost" 3819))
;#(client #Pid<0.281.0> #Pid<0.282.0>)
lfe> (ardour.client:strip-list c)
;(#(name "SynthMaster One")
; #(strip-number 1)
; #(type "MIDI track")
; #(inputs 0)
; #(outputs 2)
; #(muted? 0)
; #(soloed? 0)
; #(record-enabled? 0))
The undertone Guide
Getting Set Up
Programming Environments
Creating an undertone Project
The DSL
The Default Synth
Audio Processing
Audio Files
Creating Instruments
Samplers
Working with MIDI
Creating Music
Music Theory
The Virtual Studio
MIDI on the Mac
Controlling a DAW
Creating a Custom OSC Server
Epilogue
Versions
Current
You're looking at it.
Previous
There are currently no previous versions of the book.
Feedback and Docs or Project Bugs
If you would like to provide feedback about this guide, we would welcome the chance to improve the experience for everyone. Please create a ticket in the Github issue tracker.
If you would like to provide feedback about undertone
, you make create a ticket
in the project issue tracker.
In either case, be sure to give a full description so that we can best help you!