Skip to content

Commit adefdf8

Browse files
committed
alda standalone script
1 parent 9872da5 commit adefdf8

File tree

7 files changed

+138
-83
lines changed

7 files changed

+138
-83
lines changed

README.md

+20-6
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,37 @@ Assuming you have [Boot](http://www.boot-clj.com) installed, try this on for siz
6565

6666
git clone git@github.com:alda-lang/alda.git
6767
cd alda
68-
boot play --file test/examples/awobmolg.alda
68+
bin/alda play --file test/examples/awobmolg.alda
6969

70-
> NOTE: The first time you run the `boot play` task, you may need to wait a minute for the FluidR3 MIDI soundfont dependency (~141 MB) to download. Alda uses this soundfont in order to make your JVM's MIDI instruments sound a lot nicer. If you'd prefer to skip this step and use your JVM's default soundfont instead, include the `--stock` flag (i.e. `boot play --stock --file ...`).
70+
> NOTE: The first time you run the `play` task, you may need to wait a minute for the FluidR3 MIDI soundfont dependency (~141 MB) to download. Alda uses this soundfont in order to make your JVM's MIDI instruments sound a lot nicer. If you'd prefer to skip this step and use your JVM's default soundfont instead, include the `--stock` flag (i.e. `play --stock --file ...`).
7171
7272
You can also execute arbitrary Alda code, like this:
7373

74-
boot play --code "piano: c6 d12 e6 g12~4"
74+
bin/alda play --code "piano: c6 d12 e6 g12~4"
75+
76+
## Installation
77+
78+
The executable file `alda` in the `bin` directory of this repository is a standalone executable script that can be run from anywhere. It will retrieve the latest release version of Alda and run it, passing along any command-line arguments you give it.
79+
80+
This script requires the Clojure build tool [Boot](http://www.boot-clj.com), so you will need to have that installed first. Mac OS X users with [Homebrew](https://github.com/homebrew/homebrew) can run `brew install boot-clj` to install Boot.
81+
82+
To install Alda, simply copy the `alda` script into any directory in your `$PATH`, e.g. `/bin` or `/usr/local/bin`:
83+
84+
git clone git@github.com:alda-lang/alda.git
85+
cd alda
86+
cp bin/alda /usr/local/bin
87+
88+
You can now run `alda` from any working directory.
7589

7690
## alda.lisp
7791

7892
Under the hood, Alda transforms input (i.e. Alda code) into Clojure code which, when evaluated, produces a map of score information, which the audio component of Alda can then use to make sound. This Clojure code is written in a DSL called **alda.lisp**. See below for an example of alda.lisp code and the result of evaluating it.
7993

8094
### Parsing demo
8195

82-
You can use the `parse` Boot task to parse Alda code into alda.lisp (`-l`/`--lisp`) and/or evaluate it to produce a map (`-m`/`--map`) of score information.
96+
You can use the `parse` task to parse Alda code into alda.lisp (`-l`/`--lisp`) and/or evaluate it to produce a map (`-m`/`--map`) of score information.
8397

84-
$ boot parse --lisp --map -f test/examples/hello_world.alda
98+
$ alda parse --lisp --map -f test/examples/hello_world.alda
8599
(alda.lisp/score
86100
(alda.lisp/part {:names ["piano"]}
87101
(alda.lisp/note (alda.lisp/pitch :c)
@@ -99,7 +113,7 @@ You can use the `parse` Boot task to parse Alda code into alda.lisp (`-l`/`--lis
99113
{:events #{#alda.lisp.Note{:offset 2000.0, :instrument "piano-VoUlp", :volume 1.0, :pitch 261.6255653005986, :duration 1350.0} #alda.lisp.Note{:offset 0, :instrument "piano-VoUlp", :volume 1.0, :pitch 261.6255653005986, :duration 225.0} #alda.lisp.Note{:offset 250.0, :instrument "piano-VoUlp", :volume 1.0, :pitch 293.6647679174076, :duration 225.0} #alda.lisp.Note{:offset 1250.0, :instrument "piano-VoUlp", :volume 1.0, :pitch 349.2282314330039, :duration 225.0} #alda.lisp.Note{:offset 750.0, :instrument "piano-VoUlp", :volume 1.0, :pitch 349.2282314330039, :duration 225.0} #alda.lisp.Note{:offset 1000.0, :instrument "piano-VoUlp", :volume 1.0, :pitch 391.99543598174927, :duration 225.0} #alda.lisp.Note{:offset 1750.0, :instrument "piano-VoUlp", :volume 1.0, :pitch 293.6647679174076, :duration 225.0} #alda.lisp.Note{:offset 1500.0, :instrument "piano-VoUlp", :volume 1.0, :pitch 329.6275569128699, :duration 225.0} #alda.lisp.Note{:offset 500.0, :instrument "piano-VoUlp", :volume 1.0, :pitch 329.6275569128699, :duration 225.0}},
100114
:instruments {"piano-VoUlp" {:octave 4, :current-offset #alda.lisp.AbsoluteOffset{:offset 3500.0}, :config {:type :midi}, :duration 3N, :volume 1.0, :last-offset #alda.lisp.AbsoluteOffset{:offset 2000.0}, :id "piano-VoUlp", :quantization 0.9, :tempo 120, :panning 0.5, :current-marker :start, :stock "piano"}}}
101115

102-
$ boot parse --lisp -c 'cello: c+'
116+
$ alda parse --lisp -c 'cello: c+'
103117
(alda.lisp/score
104118
(alda.lisp/part {:names ["cello"]}
105119
(alda.lisp/note (alda.lisp/pitch :c :sharp))))

bin/alda

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env boot
2+
3+
; this is a minimal dispatch script that fetches the latest version of
4+
; alda from clojars (a maven repository for clojure projects) and runs the
5+
; alda.cli/main method, passing along any command-line arguments.
6+
7+
; this script will automatically update your version of alda as newer
8+
; versions are released.
9+
10+
(set-env! :dependencies '[[alda "LATEST"]])
11+
12+
(require '[alda.cli])
13+
14+
(defn -main [& args]
15+
(apply (resolve 'alda.cli/-main) args))

build.boot

+8-70
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,22 @@
1616

1717
(require '[adzerk.bootlaces :refer :all]
1818
'[adzerk.boot-test :refer :all]
19-
'[alda.core]
20-
'[alda.parser :refer (parse-input)]
21-
'[alda.repl])
19+
'[alda.version]
20+
'[alda.cli])
2221

23-
(def +version+ "0.3.0")
24-
(bootlaces! +version+)
22+
; version number is stored in alda.version
23+
(bootlaces! alda.version/-version-)
2524

2625
(task-options!
27-
aot {:namespace '#{alda.core}}
26+
aot {:namespace '#{alda.cli}}
2827
pom {:project 'alda
29-
:version +version+
28+
:version alda.version/-version-
3029
:description "A music programming language for musicians"
3130
:url "https://github.com/alda-lang/alda"
3231
:scm {:url "https://github.com/alda-lang/alda"}
3332
:license {"name" "Eclipse Public License"
3433
"url" "http://www.eclipse.org/legal/epl-v10.html"}}
35-
jar {:main 'alda.core}
34+
jar {:main 'alda.cli}
3635
test {:namespaces '#{alda.test.parser.attributes
3736
alda.test.parser.comments
3837
alda.test.parser.duration
@@ -49,66 +48,5 @@
4948
alda.test.lisp.score
5049
alda.test.lisp.voices}})
5150

52-
(deftask build
53-
"Builds uberjar.
54-
TODO: be able to build an executable à la lein bin"
55-
[]
56-
(comp (aot) (pom) (uber) (jar)))
57-
58-
(deftask parse
59-
"Parse some Alda code and print the results to the console."
60-
[f file FILE str "The path to a file containing Alda code."
61-
c code CODE str "A string of Alda code."
62-
l lisp bool "Parse into alda.lisp code."
63-
m map bool "Evaluate the score and show the resulting instruments/events map."]
64-
(let [alda-lisp-code (parse-input (if code code (slurp file)))]
65-
(when lisp
66-
(prn alda-lisp-code))
67-
(when map
68-
(require 'alda.lisp)
69-
(println)
70-
(prn (eval alda-lisp-code)))))
71-
72-
(defn fluid-r3!
73-
"Fetches FluidR3 dependency and returns the input stream handle."
74-
[]
75-
(clojure.core/eval
76-
'(do (merge-env!
77-
:dependencies '[[org.bitbucket.daveyarwood/fluid-r3 "0.1.1"]])
78-
(require '[midi.soundfont.fluid-r3 :as fluid-r3])
79-
fluid-r3/sf2)))
80-
81-
(deftask play
82-
"Parse some Alda code and play the resulting score."
83-
[f file FILE str "The path to a file containing Alda code."
84-
c code CODE str "A string of Alda code."
85-
; TODO: implement smart buffering and remove the buffer options
86-
p pre-buffer MS int "The number of milliseconds of lead time for buffering. (default: 0)"
87-
P post-buffer MS int "The number of milliseconds to keep the synth open after the score ends. (default: 1000)"
88-
s stock bool "Use the default MIDI soundfont of your JVM, instead of FluidR3."]
89-
(require '[alda.lisp]
90-
'[alda.sound]
91-
'[instaparse.core])
92-
(binding [alda.sound.midi/*midi-soundfont* (when-not stock (fluid-r3!))
93-
alda.sound/*play-opts* {:pre-buffer (or pre-buffer 0)
94-
:post-buffer (or post-buffer 1000)
95-
:one-off? true}]
96-
(let [parsed (parse-input (if code code (slurp file)))]
97-
(if (instaparse.core/failure? parsed)
98-
(prn parsed)
99-
(alda.sound/play! (eval parsed)))
100-
identity)))
101-
102-
(deftask alda-repl
103-
"Starts an Alda Read-Evaluate-Play-Loop."
104-
[p pre-buffer MS int "The number of milliseconds of lead time for buffering. (default: 0)"
105-
P post-buffer MS int "The number of milliseconds to wait after the score ends. (default: 0)"
106-
s stock bool "Use the default MIDI soundfont of your JVM, instead of FluidR3."]
107-
(binding [alda.sound.midi/*midi-soundfont* (when-not stock (fluid-r3!))
108-
alda.sound/*play-opts* {:pre-buffer pre-buffer
109-
:post-buffer post-buffer
110-
:async? true}]
111-
(alda.repl/start-repl! +version+)))
112-
11351
(defn -main [& args]
114-
(apply alda.core/-main args))
52+
(apply alda.cli/-main args))

src/alda/cli.clj

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
(ns alda.cli
2+
(:require [boot.cli :refer (defclifn)]
3+
[boot.core :refer (merge-env!)]
4+
[alda.parser :refer (parse-input)]
5+
[alda.repl]))
6+
7+
(defn fluid-r3!
8+
"Fetches FluidR3 dependency and returns the input stream handle."
9+
[]
10+
(eval
11+
'(do (merge-env!
12+
:dependencies '[[org.bitbucket.daveyarwood/fluid-r3 "0.1.1"]])
13+
(require '[midi.soundfont.fluid-r3 :as fluid-r3])
14+
fluid-r3/sf2)))
15+
16+
(defclifn parse
17+
"Parse some Alda code and print the results to the console."
18+
[f file FILE str "The path to a file containing Alda code."
19+
c code CODE str "A string of Alda code."
20+
l lisp bool "Parse into alda.lisp code."
21+
m map bool "Evaluate the score and show the resulting instruments/events map."]
22+
(let [alda-lisp-code (parse-input (if code code (slurp file)))]
23+
(when lisp
24+
(prn alda-lisp-code))
25+
(when map
26+
(require 'alda.lisp)
27+
(println)
28+
(prn (eval alda-lisp-code)))))
29+
30+
(defclifn play
31+
"Parse some Alda code and play the resulting score."
32+
[f file FILE str "The path to a file containing Alda code."
33+
c code CODE str "A string of Alda code."
34+
; TODO: implement smart buffering and remove the buffer options
35+
p pre-buffer MS int "The number of milliseconds of lead time for buffering. (default: 0)"
36+
P post-buffer MS int "The number of milliseconds to keep the synth open after the score ends. (default: 1000)"
37+
s stock bool "Use the default MIDI soundfont of your JVM, instead of FluidR3."]
38+
(require '[alda.lisp]
39+
'[alda.sound]
40+
'[instaparse.core])
41+
(binding [alda.sound.midi/*midi-soundfont* (when-not stock (fluid-r3!))
42+
alda.sound/*play-opts* {:pre-buffer (or pre-buffer 0)
43+
:post-buffer (or post-buffer 1000)
44+
:one-off? true}]
45+
(let [parsed (parse-input (if code code (slurp file)))]
46+
(if (instaparse.core/failure? parsed)
47+
(prn parsed)
48+
(alda.sound/play! (eval parsed)))
49+
identity)))
50+
51+
(defclifn alda-repl
52+
"Starts an Alda Read-Evaluate-Play-Loop."
53+
[p pre-buffer MS int "The number of milliseconds of lead time for buffering. (default: 0)"
54+
P post-buffer MS int "The number of milliseconds to wait after the score ends. (default: 0)"
55+
s stock bool "Use the default MIDI soundfont of your JVM, instead of FluidR3."]
56+
(binding [alda.sound.midi/*midi-soundfont* (when-not stock (fluid-r3!))
57+
alda.sound/*play-opts* {:pre-buffer pre-buffer
58+
:post-buffer post-buffer
59+
:async? true}]
60+
(alda.repl/start-repl!)))
61+
62+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
63+
64+
(def help-text
65+
"alda help text - TODO")
66+
67+
(defn- delegate
68+
[cmd args]
69+
(if (empty? args)
70+
(cmd "--help")
71+
(apply cmd args)))
72+
73+
(defn -main [& [cmd & args]]
74+
(case cmd
75+
nil (println help-text)
76+
"help" (println help-text)
77+
"--help" (println help-text)
78+
"parse" (delegate parse args)
79+
"play" (delegate play args)
80+
"repl" (delegate alda-repl args)
81+
(printf "[alda] Invalid command '%s'.\n\n%s\n" cmd)))

src/alda/core.clj

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
[alda.sound :as sound])
44
(:gen-class))
55

6+
; TODO: merge this namespace with alda.cli
7+
68
(def cli-options
79
[["-h" "--help" "Display help text."]
810
["-s" "--start START"

src/alda/repl.clj

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
(ns alda.repl
2-
(:require [alda.parser :refer (parse-input)]
2+
(:require [alda.version :refer (-version-)]
3+
[alda.parser :refer (parse-input)]
34
[alda.lisp :refer :all]
45
[alda.sound :refer (set-up! tear-down! play!)]
56
[alda.sound.midi :as midi]
@@ -25,15 +26,15 @@
2526
"██║ ██║███████╗██████╔╝██║ ██║" \newline
2627
"╚═╝ ╚═╝╚══════╝╚═════╝ ╚═╝ ╚═╝"))
2728

28-
(defn text-below-ascii-art [version]
29-
(str " v" version \newline
29+
(def text-below-ascii-art
30+
(str " v" -version- \newline
3031
" repl session"))
3132

32-
(defn banner [version]
33+
(def banner
3334
(str (blue ascii-art)
3435
\newline
3536
\newline
36-
(cyan (text-below-ascii-art version))))
37+
(cyan text-below-ascii-art)))
3738

3839
(defn parse-with-start-rule
3940
"Parse a string of Alda code starting from a particular level of the tree.
@@ -80,9 +81,9 @@
8081
prompt (str (str/join "/" abbrevs) "> ")]
8182
(.setPrompt rdr prompt)))
8283

83-
(defn start-repl! [version]
84+
(defn start-repl! []
8485
(println)
85-
(println (banner version) \newline)
86+
(println banner \newline)
8687
(let [done? (atom false)
8788
context (atom :part)
8889
reader (doto (ConsoleReader.) (.setPrompt "> "))]

src/alda/version.clj

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(ns alda.version)
2+
3+
(def ^:const -version- "0.4.0")
4+

0 commit comments

Comments
 (0)