Liking cljdoc? Tell your friends :D

Getting Started

Setup

Alda

First things first: you'll need to install Alda, if you haven't done that already.

alda-clj requires Alda version 2.0.0 or greater.

You can run alda doctor to verify that you have a working installation of Alda.

Clojure

If you don't already have clojure / clj, install the Clojure command-line tools.

There are several ways that you can set up a Clojure environment where you can use alda-clj:

  1. If you have an existing Clojure project that uses Leiningen or Boot, you can add [io.djy/alda-clj "LATEST"] to the dependencies in your project.clj or build.boot.

  2. You can start a REPL that includes alda-clj as a dependency:

    $ clj -Sdeps '{:deps {io.djy/alda-clj {:mvn/version "LATEST"}}}'
    Downloading: io/djy/alda-clj/maven-metadata.xml from https://repo.clojars.org/
    Downloading: io/djy/alda-clj/0.3.1/alda-clj-0.3.1.pom from https://repo.clojars.org/
    Downloading: io/djy/alda-clj/0.3.1/alda-clj-0.3.1.jar from https://repo.clojars.org/
    Clojure 1.10.1
    user=> (require '[alda.core :refer :all])
    nil
    user=> (alda "version")
    alda 2.0.0
    "alda 2.0.0\n"
    
  3. You can write all of your Clojure code in a single file:

    $ cat demo.clj
    (require '[alda.core :refer :all])
    
    (alda "version")
    
    (System/exit 0)
    $ clojure -Sdeps '{:deps {io.djy/alda-clj {:mvn/version "LATEST"}}}' demo.clj
    alda 2.0.0
    
  4. You can make a new Clojure project that includes alda-clj as a dependency by creating a directory structure that looks like this...

    .
    ├── deps.edn
    └── src
        └── something.clj
    

    ...putting the following in deps.edn...

    {:deps {io.djy/alda-clj {:mvn/version "LATEST"}}}
    

    ...and putting your Clojure code in src/something.clj...

    (ns something
      (:require [alda.core :refer :all]))
    
    (defn -main
      []
      (alda "version")
      (System/exit 0))
    

    ...and then run the main function:

    $ clojure -m something
    alda 2.0.0
    

Once you're set up, you can follow along by copy-pasting the examples below into your Clojure source file or REPL.

Basics

Each element of Alda's syntax has a corresponding function in alda.core.

For example, the following Alda score:

piano:
  c8 d e f g a b > c

...can be produced via alda-clj by using the corresponding functions in alda.core:

(require '[alda.core :refer :all])

(part "piano")
(note (pitch :c) (note-length 8))
(note (pitch :d))
(note (pitch :e))
(note (pitch :f))
(note (pitch :g))
(note (pitch :a))
(note (pitch :b))
(octave :up)
(note (pitch :c))

But the code above doesn't actually do anything. Each of the forms simply returns a Clojure record representing an Alda event such as a note, an octave change, a part declaration, etc.

You can turn this into a score and play it by passing the events as arguments to alda.core/play!:

(play!
  (part "piano")
  (note (pitch :c) (note-length 8))
  (note (pitch :d))
  (note (pitch :e))
  (note (pitch :f))
  (note (pitch :g))
  (note (pitch :a))
  (note (pitch :b))
  (octave :up)
  (note (pitch :c)))

The return value of the play! form is the string of Alda code that was generated and sent to the Alda client:

"piano:\nc8 d e f g a b > c"

You can always stop playback if things get out of hand:

(stop!)

Shelling out

You can conveniently issue arbitrary commands to the Alda client from the comfort of your REPL:

;; like running "alda version" in a terminal
(alda "version")

;; like running "alda doctor" in a terminal
(alda "doctor")

;; like running "alda instruments" in a terminal
(alda "instruments")

The output is printed on stdout and stderr, just like if you were running the commands in a terminal.

If the command was successful (i.e. the exit code was 0), the full output on stdout is also returned as a string, for convenience.

Connecting to an Alda REPL server

By default, alda-clj is not connected to an Alda REPL server. You can still use play! to play scores, like we did above, however, each time you call play!, the score is evaluated in a separate context.

For a better understanding of what this means, try evaluating the following expressions, one at a time:

;; Play a few notes on the guitar.
(play!
  (part "guitar")
  (note (pitch :e) (note-length 8))
  (note (pitch :f :sharp))
  (note (pitch :g)))

;; Play a couple more notes on the guitar?
(play!
  (note (pitch :a))
  (note (pitch :b) (note-length 2)))

Unless you are connected to an Alda REPL, you will only hear the notes in the first expression. The second expression will run successfully, but you won't hear anything. Why? Because the second expression is equivalent to running the following alda play command at the command line:

alda play --code "a b2"

And no instrument is specified, so it makes no sound.

Wouldn't it be nice if you could play your score a little bit at a time? Well, if you connect to an Alda REPL server, then you can do exactly that!

To start an Alda REPL server, run alda repl --server in your terminal. You should see some output that tells you which port the server is running on:

nREPL server started on port 40105 on host localhost - nrepl://localhost:40105

Now, in your Clojure REPL (or program), use the connect! function to connect to the Alda REPL server:

;; Replace 40105 with the port that was printed when you started the Alda REPL
;; server.
(connect! {:port 40105})

If you started both your Clojure REPL/program and your Alda REPL server in the same directory, you can omit the port argument and alda-clj will figure it out by reading a file created by the Alda REPL server (.alda-nrepl-port).

(connect!)

Now, you can write and play your scores incrementally!

;; Conjure a guitar.
(play!
  (part "guitar"))

;; Play a few notes on the guitar.
(play!
  (note (pitch :e) (note-length 8))
  (note (pitch :f :sharp))
  (note (pitch :g)))

;; Play a couple more notes, still on the guitar.
(play!
  (note (pitch :a))
  (note (pitch :b) (note-length 2)))

Between invocations of play!, the Alda REPL server is keeping track of which instrument(s) are active and all of their properties (octave, volume, panning, etc.) so that the context is not lost. This is useful for live coding, as well as for quickly trying things out when you're composing a score.

Whenever you want to erase this context and start over from a clean slate, you can reset the state of the Alda REPL server:

(new-score!)

To see the Alda code that has been evaluated so far in your current score:

(println (score-text))

Notes

The note function takes as arguments a pitch and (optionally) a duration.

;; a D
(play!
  (note (pitch :d)))
;;=> "d"

;; an E eighth note
(play!
  (note (pitch :e) (note-length 8)))
;;=> "e8"

;; a C# whole note tied to a dotted half note
(play!
  (note (pitch :c :sharp)
        (duration (note-length 1)
                  (note-length 2 {:dots 1}))))
;;=> "c+1~2."

Pitch

The pitch function can be used to construct a pitch out of a letter and any number of accidentals (flats and sharps):

(play!
  (note (pitch :b)))
;;=> "b"

(play!
  (note (pitch :b :flat)))
;;=> "b-"

(play!
  (note (pitch :f :sharp)))
;;=> "f+"

(play!
  (note (pitch :f :sharp :sharp)))
;;=> "f++"

Duration

A simple example of a duration is a single note-length:

;; a C half note
(play!
  (note (pitch :c) (note-length 2)))
;;=> "c2"

Alda also allows you to specify the length of a note in milliseconds:

;; a C note lasting 423 ms
(play!
  (note (pitch :c) (ms 423)))
;;=> "c423ms"

Durations (whether they be note-length or ms) can be added together via the duration function:

;; A double-dotted quarter note, tied to an eighth note, tied to a note 456ms
;; long, tied to a "sixth" note. Try writing THAT in standard musical notation!
(play!
  (note (pitch :c)
        (duration (note-length 4 {:dots 2})
                  (note-length 8)
                  (ms 456)
                  (note-length 6))))
;;=> "c4..~8~456ms~6"

Rests

The function for a rest in alda.core is called pause, so as not to conflict with the super popular function clojure.core/rest:

(play!
  (note (pitch :f) (note-length 4))
  (pause (note-length 4))
  (note (pitch :f) (note-length 4))
  (pause (note-length 4))
  (note (pitch :f) (note-length 4))
  (pause (note-length 4)))
;;=> "f4 r4 f4 r4 f4 r4"

Chords

Wrap notes in a chord form to create a chord:

(play!
  (chord
    (note (pitch :c) (note-length 1))
    (note (pitch :e :flat))
    (note (pitch :g))
    (note (pitch :b))))
;;=> "c1 / e- / g / b"

Strings and sequences

Ultimately, all we're doing here is generating a string of Alda code and sending it over to the Alda client.

Because each event just ends up being a string anyway, play! will happily accept a string of Alda code in lieu of an event:

(play!
  "piano: o4"
  "c16/e/g"
  (note (pitch :a))
  (note (pitch :b))
  "> c")
;;=> "piano: o4 c16/e/g a b > c"

For convenience, play! will also flatten the sequence of its arguments. This means you can make liberal use of the handy sequence functions in Clojure's standard library, without needing to worry about providing play! with a flat sequence of events.

(play!
  (part "piano")
  (for [t (repeatedly 4 #(+ 60 (rand-int 200)))]
    [(tempo t)
     (for [letter [:c :d :e :f :g]]
       (note (pitch letter) (note-length 8)))]))
;;=> "piano:\n(tempo 72) c8 d8 e8 f8 g8 (tempo 157) c8 d8 e8 f8 g8 (tempo 129) c8 d8 e8 f8 g8 (tempo 96) c8 d8 e8 f8 g8"

Inline Lisp forms

The Alda language itself includes Lisp forms as a syntactic feature. This is used to provide language features without needing to provide additional syntax. For example, here is how you set the global tempo in an Alda score:

(tempo! 160)

piano: c8 d e f g

alda-clj allows code like this to be generated in a couple of different ways. One way is that you can pass quoted S-expressions into play! and they will be emitted verbatim into the string of generated Alda code:

(play!
  '(tempo! 160)
  (part "piano")
  (note (pitch :c) (note-length 8))
  (note (pitch :d))
  (note (pitch :e))
  (note (pitch :f))
  (note (pitch :g)))
;;=> "(tempo! 160) piano:\nc8 d e f g"

Another way is that alda.core includes convenience functions for the functions like tempo! that are available in the Alda Lisp environment. Whenever alda.core includes one of these functions, you can simply call it and it will have the same effect as emitting the S-expression directly into the generated Alda code:

(play!
  (tempo! 160)
  (part "piano")
  (note (pitch :c) (note-length 8))
  (note (pitch :d))
  (note (pitch :e))
  (note (pitch :f))
  (note (pitch :g)))
;;=> "(tempo! 160) piano:\nc8 d e f g"

Other stuff

alda-clj supports every feature of the Alda language, including things not covered here like cram brackets, variables, and markers.

The API docs provide a reference of what functions are available in alda.core and how they are used.

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close