Alda
A Music Programming Language
Built With Clojure
Clojure Remote 2016
Dave Yarwood
@dave_yarwood
This Talk
- Background on Music Programming Languages
- Basic introduction to Alda
- Broad overview of the "moving parts"
- Alda + Clojure = <3
- Dave's secret plot to lure contributors
+
=
Music programming?
Sibelius: a graphical score editor
(omg we're famous!)
github.com/alda-lang/alda
DEMO TIME
Alda: Basic Anatomy
Java client
Server
Parser
REPL
Sound Engine
Clojure DSL
The Alda Client
$ alda up
[27713] Starting Alda server...
[27713] Server up ✓
Java/Clojure inter-op
Utility function for calling a Clojure function in the same project:
public static void callClojureFn(String fn, Object... args) {
Symbol var = (Symbol)Clojure.read(fn);
IFn require = Clojure.var("clojure.core", "require");
require.invoke(Symbol.create(var.getNamespace()));
ISeq argsSeq = ArraySeq.create(args);
Clojure.var(var.getNamespace(), var.getName()).applyTo(argsSeq);
}
Util.callClojureFn("alda.server/start-server!", args);
Using it to start an Alda server:
The Alda
Sound Engine
Data Representation of a Note
{:offset 1000.0, :instrument "piano-ERbWJ", :volume 1.0,
:track-volume 0.7874015748031497, :panning 0.5, :midi-note 64,
:pitch 329.6275569128699, :duration 450.0}
- Instrument ID
- Pitch (frequency in Hz)
- MIDI note (0-127)
- Duration (ms)
- Note length (quarter, eighth, etc.)
- Tempo (beats per minute)
- Offset (ms)
- Volume (0.0-1.0)
- Panning (0.0-1.0)
Data Representation of a Score
{:events
#{{:offset 1000.0, :instrument "piano-ERbWJ", :volume 1.0,
:track-volume 0.7874015748031497, :panning 0.5, :midi-note 64,
:pitch 329.6275569128699, :duration 450.0}
{:offset 500.0, :instrument "piano-ERbWJ", :volume 1.0,
:track-volume 0.7874015748031497, :panning 0.5, :midi-note 62,
:pitch 293.6647679174076, :duration 450.0}
{:offset 0, :instrument "piano-ERbWJ", :volume 1.0,
:track-volume 0.7874015748031497, :panning 0.5, :midi-note 60,
:pitch 261.6255653005986, :duration 450.0}},
:markers {:start 0},
:instruments
{"piano-ERbWJ"
{:octave 4, :current-offset {:offset 1500.0}, :key-signature {},
:config {:type :midi, :patch 1}, :duration 1, :volume 1.0,
:last-offset {:offset 1000.0}, :id "piano-ERbWJ", :quantization 0.9,
:tempo 120, :panning 0.5, :current-marker :start,
:stock "midi-acoustic-grand-piano",
:track-volume 0.7874015748031497}}}
softsynth.com/jsyn
JSyn
javax.sound.midi
alda.lisp
(The Alda Clojure DSL)
alda.lisp
(score
(part {:names ["piano"]}
(note (pitch :c) (duration (note-length 8)))
(note (pitch :e))
(note (pitch :g))
(chord
(note (pitch :d) (duration (note-length 2 {:dots 1})))
(note (pitch :f))
(note (pitch :a)))))
alda.parser=> (parse-input "piano: c8 e g d2./f/a")
(alda.lisp/score
(alda.lisp/part {:names ["piano"]}
(alda.lisp/note (alda.lisp/pitch :c)
(alda.lisp/duration (alda.lisp/note-length 8)))
(alda.lisp/note (alda.lisp/pitch :e))
(alda.lisp/note (alda.lisp/pitch :g))
(alda.lisp/chord
(alda.lisp/note (alda.lisp/pitch :d)
(alda.lisp/duration (alda.lisp/note-length 2 {:dots 1})))
(alda.lisp/note (alda.lisp/pitch :f))
(alda.lisp/note (alda.lisp/pitch :a)))))
alda.now
(require '[alda.lisp :refer :all])
(require '[alda.now :refer (set-up! play!)])
(score*)
(part* "upright-bass")
; This is optional. If left out, Alda will set up the MIDI synth the first
; time you tell it to play something.
(set-up! :midi)
(play!
(octave 2)
(note (pitch :c) (duration (note-length 8)))
(note (pitch :d))
(note (pitch :e))
(note (pitch :f))
(note (pitch :g) (duration (note-length 4))))
Inline Clojure Code
(def REST-RATE 0.15)
(def MS-LOWER 30)
(def MS-UPPER 3000)
(def MAX-OCTAVE 8)
(defn random-note
"Plays a random note in a random octave,
for a random number of milliseconds.
May randomly decide to rest, instead,
for a random number of milliseconds."
[]
(let [ms (ms (rand-nth (range MS-LOWER MS-UPPER)))]
(if (< (rand) REST-RATE)
(pause (duration ms))
(let [o (rand-int (inc MAX-OCTAVE))
n [(keyword (str (rand-nth "abcdefg")))
(rand-nth [:sharp :flat :natural])]]
(octave o)
(note (apply pitch n) (duration ms))))))
# continued from left
midi-electric-piano-1:
(panning 25)
(random-note) * 20
midi-timpani:
(panning 50)
(random-note) * 20
midi-celesta:
(panning 75)
(random-note) * 20
entropy.alda
What's next?
- Microtonal music
- Waveform synthesis
- Variables
riffA = f8 f g+ a > c c d c <
riffB = b-8 b- > c+ d f f g f <
riffC = > c8 c d+ e g g a g <
riffD = f8 f g+ a > c c d < b > | c c < b- b- a a g g
riffA*4
riffB*2 riffA*2 # or riffB** riffA**
riffC riffB riffD
riffA # expands to "f8 f g+ a > c c d c <"
rockinRiff = [
riffA*4
riffB*2 riffA*2
riffC riffB riffD
]
guitar/saxophone:
rockinRiff*9999999
What's next?
- Modules
- Export to MusicXML, MIDI, LilyPond, etc.
- Import from MusicXML, MIDI, LilyPond, etc.
- Export audio to file
- Plugin system
THANKS
waffle.io/alda-lang/alda
Want to get in on this?
We could use your help!
github.com/
Try Alda
Slide graveyard
I over-planned and ended up with way more slides than I had time to present.
Stashing the slides I got rid of here.
Music Macro Language (MML)
1 PRINT "TOORYANSE"
2 PRINT "ARRANGED BY"
3 PRINT " (C)2012 MOTOI KENKICHI"
4 PRINT " THANKS ALL WIKIPEDIANS."
10 TEMPO 4
20 A$="E5R1E3R0D3R0E3R0E1R0D1R0-G4R1"
30 B$="F3R0F1R0F1R0A3R0F1R0E1R0D1R0D1R0E5R0"
40 C$="C3R0C1R0C1R0E3R0C1R0-B1R0C1R0-B1R0-A1R0-A1-B5R0"
50 D$="E1R0E1R0E1R0E1R0E1R0E1R0D1R0E1R0E1R0E1R0D1R0-A1R0-A1R0B3R1"
60 E$="-A1R0-B1R0C1R0D1R0E1R0F1R0E1R0F3R1A3R1B1R0A1R0F3R0E3R0E1R0E4R0"
100 MUSIC A$+B$+B$
110 MUSIC C$+C$+B$
120 MUSIC C$+D$+E$
(Classical MML, est. 1978)
Music Macro Language (MML)
#TITLE My First NES Chip
#COMPOSER Nullsleep
#PROGRAMER 2003 Jeremiah Johnson
@v0 = { 10 9 8 7 6 5 4 3 2 }
@v1 = { 15 15 14 14 13 13 12 12 11 11 10 10 9 9 8 8 7 7 6 6 }
@v2 = { 15 12 10 8 6 3 2 1 0 }
@v3 = { 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 }
ABCDE t150
A l8 o4 @01 @v0
A [c d e f @v1 g4 @v0 a16 b16 >c c d e f @v1 g4 @v0 a16 b16 >c<<]2
C l4 o3 q6
C [c e g8 g8 a16 b16 >c8 c e g8 g8 a16 b16 >c8<<]4
D l4 o1 @0 @v2
D [@v2 b @v3 e @v2 b @v3 e @v2 b @v3 e @v2 b @v3 e8 @v2 b8]4
(Modern MML, est. 2001)
LilyPond
\relative c, {
\clef "bass"
\time 3/4
\tempo "Andante" 4 = 120
c2 e8 c'
g'2.
f4 e d
c4 c, r
}
my_score.ly
my_score.pdf
Csound
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1
instr 1
iflg = p4
asig oscils .7, 220, 0, iflg
outs asig, asig
endin
</CsInstruments>
<CsScore>
i 1 0 2 0
i 1 3 2 2
e
</CsScore>
</CsoundSynthesizer>
Using JSyn to schedule events
(import '[com.softsynth.shared.time TimeStamp ScheduledCommand]
'[com.jsyn.engine SynthesisEngine])
(def engine
(doto (SynthesisEngine.) .start))
Define events:
(defn event [f]
(reify ScheduledCommand
(run [_]
(f))))
(def hello-event (event #(println "hello")))
(def clojure-event (event #(println "clojure")))
(def remote-event (event #(println "remote")))
Schedule events:
(let [start (.getCurrentTime engine)]
(.scheduleCommand engine (TimeStamp. (+ start 1.0)) hello-event)
(.scheduleCommand engine (TimeStamp. (+ start 1.5)) clojure-event)
(.scheduleCommand engine (TimeStamp. (+ start 2.0)) remote-event))
javax.sound.midi
javax.sound.midi
javax.sound.midi
javax.sound.midi
javax.sound.midi
(import '[javax.sound.midi MidiSystem])
(def synth
(doto (MidiSystem/getSynthesizer) .open))
(def channel
(aget (.getChannels synth) 0))
(defn play-note [chan n]
(.noteOn chan n 127)
(Thread/sleep 300)
(.noteOff chan n))
; set instrument to slap bass
(.programChange channel 36)
; slap a C major scale
(doseq [n [36 38 40 41 43 45 47 48]]
(play-note channel n))
Initialize a synth and get the first channel:
Change patches, play notes:
Alda: A Music Programming Language, Built in Clojure
By Dave Yarwood
Alda: A Music Programming Language, Built in Clojure
Inspired by other music/audio programming languages such as PPMCK, LilyPond and ChucK, Alda aims to be a powerful and flexible programming language for the musician who wants to easily compose and generate music on the fly, using only a text editor. Clojure proved to be an ideal language for building a language like Alda, not only because of its wealth of excellent libraries like Instaparse and Overtone, but also because of its Lispy transparency and facility for crafting DSLs.
- 2,866