Fixed #255. Voices can now be used inside of event sequences and variable definitions.
Thanks, bbqbaron, for your work on fixing this issue!
Fixed #242. In rare cases, error messages were sometimes hidden due to the way we were formatting them inside of the function that displays the error message.
Thanks to bbqbaron for the PR to fix this issue!
This release includes improvements to the system that assigns instrument parts and groups in an Alda score (#249). Thanks, elyisgreat and jimcheetham, for helping to talk me through all the different scenarios!
The documentation has also been updated and improved. We hope this will make the rules of instrument part and group assignment easier to understand and remember from here on out.
Errors are now thrown in the following scenarios, which prior to this release would lead to undefined or confusing behavior:
Calls like foo "bar"
where foo
is a previously defined nickname (e.g. piano "foo"
), not a stock instrument.
Mixing and matching unnamed vs. named instances of the same instrument in a score.
Mixing and matching stock instruments and named instances in the same group. Groups are now explicitly only for grouping named (existing) instances or creating new ones.
There is a list of simple rules with examples in the documentation, which I think is the best way to understand valid vs. invalid usage of parts in an Alda score.
For the curious, there is also a comprehensive explanation of what happens in different scenarios.
There is a new dot (.
) operator for accessing members of groups:
violin/viola/cello "strings": c8 d e f
strings.cello: g1
This release includes a handful of behind-the-scenes improvements that will make it easier for us to develop Alda.
The output of alda status
now includes the server's backend port number. This is the port that the server uses to communicate with its worker processes.
Added automated integration tests. These tests can be run alone with boot test --integration
, or along with the existing unit tests with boot test --all
.
Both the alda.server
and alda.worker
namespaces now include a *no-system-exit*
dynamic var that is false
by default, but can be bound to true
in situations where you're running a server or worker within a Clojure program and you don't necessarily want the entire process to exit when the server or worker does. This makes it possible to run the suite of integration tests without being interrupted by a System.exit
from a server or worker thread.
The server now recognizes a new message, DONE
, that can be sent by a worker process to indicate that it is done working and should not be considered available. When the server receives a DONE
message from a worker, it removes that worker from its queue, thus ensuring that it will not use that worker to handle any subsequent client requests.
This message is not currently used during the lifetime of a "production" Alda worker process, but it ended up being useful for the integration test suite.
The -v
/ --verbose
option will now provide debug output when starting a server
or worker
process in the foreground:
$ alda -v -p 27714 server
16-Oct-08 16:43:24 skeggox.local INFO [alda.server] - Binding frontend socket on port 27714...
16-Oct-08 16:43:24 skeggox.local INFO [alda.server] - Binding backend socket on port 56885...
16-Oct-08 16:43:24 skeggox.local INFO [alda.server] - Spawning 2 workers...
16-Oct-08 16:43:45 skeggox.local DEBUG [alda.server] - Receiving message from frontend...
16-Oct-08 16:43:45 skeggox.local DEBUG [alda.server] - Forwarding message to worker 00B757D1FC...
16-Oct-08 16:43:45 skeggox.local DEBUG [alda.server] - Forwarding backend response to frontend...
$ alda -v -p 56885 worker
Oct 08, 2016 4:45:40 PM com.jsyn.engine.SynthesisEngine start
INFO: Pure Java JSyn from www.softsynth.com, rate = 44100, RT, V16.7.3 (build 457, 2014-12-25)
16-Oct-08 16:45:41 skeggox.local INFO [alda.worker] - Worker reporting for duty!
16-Oct-08 16:45:41 skeggox.local INFO [alda.worker] - Connecting to socket on port 50292...
16-Oct-08 16:45:41 skeggox.local INFO [alda.worker] - Sending READY signal.
16-Oct-08 16:45:42 skeggox.local DEBUG [alda.worker] - Got HEARTBEAT from server.
16-Oct-08 16:45:43 skeggox.local DEBUG [alda.worker] - Got HEARTBEAT from server.
16-Oct-08 16:45:44 skeggox.local DEBUG [alda.worker] - Got HEARTBEAT from server.
16-Oct-08 16:45:45 skeggox.local DEBUG [alda.worker] - Got HEARTBEAT from server.
16-Oct-08 16:45:46 skeggox.local DEBUG [alda.worker] - Got HEARTBEAT from server.
16-Oct-08 16:45:46 skeggox.local DEBUG [alda.worker] - Processing message...
16-Oct-08 16:45:46 skeggox.local ERROR [alda.worker] - java.lang.Exception: Parse error at line 1, column 19:
(throw (Exception.
^
Expected one of:
")"
"("
"\\"
"\""
#".|\n|\r"
alda.Main.main Main.java: 230
alda.AldaWorker.upFg AldaWorker.java: 12
alda.Util.callClojureFn Util.java: 263
...
alda.worker/start-worker! worker.clj: 136
alda.worker/start-worker! worker.clj: 151
alda.worker/start-worker!/fn worker.clj: 188
alda.worker/start-worker!/fn/fn worker.clj: 191 ... alda.worker/eval8377/fn worker.clj: 95 alda.worker/handle-code-parse worker.clj: 72 alda.parser/parse-input parser.clj: 284 alda.parser/remove-comments parser.clj: 109 alda.parser/check-for-failure parser.clj: 54 java.lang.Exception: Parse error at line 1, column 19: (throw (Exception. ^ Expected one of: ")" "(" "\" """ #".|\n|\r"
16-Oct-08 16:45:46 skeggox.local DEBUG [alda.worker] - Sending response... 16-Oct-08 16:45:46 skeggox.local DEBUG [alda.worker] - Response sent. 16-Oct-08 16:45:47 skeggox.local DEBUG [alda.worker] - Got HEARTBEAT from server. 16-Oct-08 16:45:48 skeggox.local DEBUG [alda.worker] - Got HEARTBEAT from server.
## 1.0.0-rc43 (9/25/16)
* Tuned JVM performance to use less unnecessary memory and CPU. See issue [#269](https://github.com/alda-lang/alda/issues/269) for more context, but the TL;DR version is that prior to this release, the Alda server and worker processes were each provisioned with more memory and CPU settings than they needed, causing them to wastefully use up memory/CPU resources that your computer could otherwise be using.
Big thanks to [feldoh] for this contribution!
## 1.0.0-rc42 (9/24/16)
* Implemented an internal "worker status" system so that the Alda client has better visibility into the status of the worker process handling a request to play a score. This only affects the `alda play` command.
`alda play` requests that take longer than 3000 milliseconds will no longer time out and result in an incorrect error about the server being "down." The way it works now is more asynchronous:
- The client makes a request to play a large score.
- A worker gets the request and responds immediately. The server includes a note about which worker it is so that the client can follow up with the worker for status.
- The client repeatedly sends requests for the status of that worker, and the worker asynchronously responds to let the client know if it is parsing, playing, done, or if there was some error.
- As the client receives updates, it prints them to the console:
```bash
$ alda play -c 'bassoon: (Thread/sleep 5000) o2 d1~1~1~1~1'
[27713] Parsing/evaluating... # immediate response
[27713] Playing... # 5 seconds later
The focus of this release is to use less CPU when starting an Alda server and worker processes. Thanks to 0atman for reporting this issue!
Prior to this release, when you started up and Alda server, it would simultaneously start all of its worker processes, which was heavy on the CPU. It turns out that waiting 10 seconds between starting each worker decreases CPU usage significantly, so that's what we're doing now.
Because it can take a little longer now for all workers to start (add an additional 10 seconds for each worker after 1), the server will now check on the current number of workers every 60 seconds instead of every 30 seconds.
The default number of workers is now 2 instead of 4. This uses significantly less CPU, and should be adequate for most Alda users. Remember that if you desire more workers, you can use the --workers
option when starting the server, e.g. alda --workers 4 up
for 4 workers.
Removed the temporary --cycle-workers
option introduced in 1.0.0-rc39. Now that CPU usage will not be as much of a problem, it is safer to cycle workers by default.
ERROR Invalid Alda syntax.
. Now it will report the error message. (This may have been working at some point and then regressed in a recent release. Sorry about that!)There is a CPU usage issue that can cause poor performance when cycling out workers, a feature that was implemented in the last release.
The real solution is going to be to fix the CPU usage problem, but in the meantime, this release makes the worker-cycling behavior introduced in the last release an opt-in feature.
To opt in, include the --cycle-workers
option when starting the server:
alda --cycle-workers up
Note that even when not using this option, the worker cycling will still eventually happen (within 30 seconds of your computer coming out of suspended state). When it has been 10+ seconds since the worker was last active (e.g. if your computer was in suspended state), each worker will detect this and shut itself down. Every 30 seconds, the server checks to make sure it has the correct number of workers, so it will replace the workers that went down.
The change with this release is that the workers will not immediately be replaced upon coming out of suspended state. It would be better if they were immediately replaced, but we will have to fix the CPU usage problem first. (If you're good at profiling / determining the cause of high CPU usage, we could use your help!)
Fixed issue #160, re: MIDI audio being delayed after suspending and bringing back Alda processes, e.g. after closing and re-opening your laptop lid.
Now that we have a server/workers architecture, the solution to this is for the server and workers to each detect when the system has been suspended, and act accordingly:
This is not a perfect solution in that if you close your laptop and then reopen it in under 10 seconds, the suspension may not be properly detected, so you might still have the same "bad" workers as before with delayed audio. If you ever observe this behavior, you can always fix it by restarting the server and workers via alda downup
.
Fixed a minor bug where workers may be sending too many heartbeats, potentially resulting in poor performance. Now they should only send heartbeats once per second.
Continuing from the ulimit -n
-related alda up
issues noted in the previous release, this makes it so that we check the number of available workers every 250 ms instead of 100 ms. This does not solve the "Too many open files" issue, but as a workaround it appears to stop the error from happening when your ulimit -n
is 256 and you're starting the default number (4) of workers.
See issue #261 for further discussion.
This release makes a handful of minor improvements to the way we're managing socket connections. The goal is avoid the java.io.IOException: Too many open files
error, (issue #261) which can occur when Alda tries to open more socket connections than your system allows.
Updated to JeroMQ 0.3.5, which includes some behind-the-scenes resource management improvements.
Ensured that any time Alda forks a background process, it closes its input, output, and error streams in order to free up those resources that we aren't using.
When you run alda up
, the client repeatedly sends requests to get an update on when the server is up so that it can notify you, then it does the same thing with the worker processes. The worker processes can take a while to spin up, so the client ends up sending a lot of requests to the server for the status of the worker processes. Prior to this release, each of these requests created a new connection, causing the "open files" limit to be quickly met. As of this release, a single Alda CLI command (like alda up
) will only create a single connection and reuse it.
I tested this release by setting my ulimit -n
to 256 and saw some definite improvement, but still occasionally saw the "Too many open files" error happening, especially when trying to use 4 Alda worker processes. I'll continue to tinker with this and see if there are other improvements to be made.
If you have a low
ulimit -n
(256 is quite low; I would recommend 10240 at least) and would like to get better performance from Alda, you can set it higher for your current terminal session by runningulimit -n 10240
. However, this setting will go away once you close your terminal window, so if you're looking for a more permanent solution, the Riak docs happen to have an excellent description of how to set your open file limit more permanently.
Quick TL;DR of what you should do when updating to this release:
- Before updating, run
alda down
to stop your Alda server if you have one running. If you don't do this before updating, you may need to find and kill the process manually.- Run
alda update
to update.alda start
,alda stop
andalda restart
have been renamed. From now on, you will need to use the commandsalda up
,alda down
andalda downup
oralda start-server
,alda stop-server
andalda restart-server
instead. See below for more details about this change.
This release adds more internal architecture improvements, further leveraging ZeroMQ to break out the functionality of Alda into separate, specialized background processes.
Prior to this release, Alda was a two-process system:
To help with performance issues related to playing multiple scores at once, each request is now handled by a separate worker process. The client/server relationship is still exactly the same; as an Alda user, you never need to communicate directly with the workers or worry about them in any way; they are solely the responsibility of the server.
When starting Alda with alda up
, you will now see the server come up much faster than before, followed by a more typical wait for the first worker to become available:
$ alda up
[27713] Starting Alda server...
# short wait
[27713] Server up ✓
[27713] Starting worker processes...
# longer wait
[27713] Ready ✓
When you see the "Ready" line, you will be able to use Alda the same as before, e.g.:
$ alda play -c 'bassoon: o2 d8 e (quant 33) f+ g (quant 90) a2'
[27713] Playing...
You can safely leave the server running in the background and it will manage its own supply of workers, replacing any that become unresponsive. Similarly, a worker process will shut itself down if the server becomes unresponsive for any reason.
By default, an Alda server maintains a pool of 4 workers. If you try to play more than 4 scores at the same time, the server will respond that there are no workers available and ask you to wait until the next worker becomes available:
$ alda play -c 'bassoon: o2 d1~1~1~1~1'
[27713] Playing...
$ alda play -c 'bassoon: o2 d1~1~1~1~1'
[27713] Playing...
$ alda play -c 'bassoon: o2 d1~1~1~1~1'
[27713] Playing...
$ alda play -c 'bassoon: o2 d1~1~1~1~1'
[27713] Playing...
$ alda play -c 'bassoon: o2 d1~1~1~1~1'
[27713] ERROR All worker processes are currently busy. Please wait until playback is complete and try again.
If you desire more or fewer workers, you can use the -w
/--workers
option when starting the server:
$ alda -w 6 up
[27713] Starting Alda server...
[27713] Server up ✓
[27713] Starting worker processes...
[27713] Ready ✓
Be aware, however, that running a larger amount of servers uses more of your CPU. I think the default of 4 workers should be comfortable for most users.
Running alda status
will now show you the number of workers currently available:
$ alda status
[27713] Server up (4/4 workers available)
$ alda play -f examples/hello_world.alda
[27713] Playing...
# one worker is busy playing hello_world.alda
$ alda status
[27713] Server up (3/4 workers available)
# after playback completes, the worker is available again
$ alda status
[27713] Server up (4/4 workers available)
Running alda list
will now list all Alda processes, including servers and workers.
This command is currently only available on Unix/Linux systems.
Please let us know if you can help us implement the same functionality for Windows!
$ alda list
[27713] Server up (4/4 workers available)
[58678] Worker (pid: 85804)
[58678] Worker (pid: 85805)
[58678] Worker (pid: 85806)
[58678] Worker (pid: 85807)
If an Alda worker process ever encounters an error, it logs the error to ~/.alda/logs/error.log
. The logs in this folder are rotated weekly, making cleanup easy if you do not want to keep old error logs.
Fixed issue #243. Prior to the infrastructure improvements made in this release, the server was a single process trying to play multiple scores at once, which caused the sound to break up. Having each worker be a separate process that only handles one score at a time results in more reliable playback. Thanks to goog for reporting this issue!
Fixed issue #258. This issue was also related to the previous single-server/worker system. While busy handling one request, the server was not able to handle the next right away, causing the request to be re-submitted and handled more than once, resulting in scores playing more than once with unexpected timing. Now that the server's sole responsibility is to maintain worker processes and forward them requests, it can handle requests a lot faster, and the client no longer needs to submit them more than once. Thanks to elyisgreat for reporting this issue!
In order to enable the new server/workers architecture, we had to remove a handful of commands that relied on "stateful server" behavior. An Alda server no longer has a notion of the "current score" you are working with. Instead, the state of your score should be maintained in the form of Alda code in a file; the functionality of the removed commands is better left up to your file system and text editor.
The following commands have been removed:
alda play
(without a --file
or --code
argument, this command used to play the "current score" in memory on the server)alda play --append
(this used to play a file or string of code and append it to the "current score").alda append
/ (same as alda play --append
, but just appended to the "current score" without playing the new code)alda score
(used to display the "current score")alda info
(used to display information about the "current score")alda new
(used to delete the "current score" and start a new one)alda load
(used to replace the "current score" with the contents of a file or string of code)alda save
(used to save the "current score" to a file)alda edit
(used to open the "current score" in your text editor of choice)In anticipation of a future alda stop
command that will stop playback instead of stopping the server, the previous alda stop
command (which would stop the server) has been renamed alda stop-server
. By analogy, alda start
has been renamed to alda start-server
and alda restart
has been renamed to alda restart-server
. I recommend using the shorter aliases alda up
, alda down
and alda downup
to start, stop, and restart the server.
This release adds a few safeguards against inadvertently starting more than one server process for the same port and ending up in a situation where you have potentially many Alda server processes hanging around, only one of which will be able to serve on that port. Thanks to elyisgreat for reporting this issue (#258).
Safeguards include:
Increasing the "server startup timeout" back to 30 seconds. It turns out that I accidentally lowered it to 15 seconds in 1.0.0-rc32, and on some computers an Alda server can take longer than 15 seconds to start. 30 seconds seems like a better default.
This timeout is the number of seconds, after running alda up
, before the client gives up on waiting for the server to start, assumes something went wrong, and tells you that the server is down.
If your computer is particularly slow and it takes longer than 30 seconds to start an Alda server, you can increase the timeout by supplying a new global option -t
/ --timeout
:
alda --timeout 45 up # wait 45 seconds before giving up
This should only be necessary on the slowest of computers, but the option is there if you need it.
If you do experience a timeout and you try to start the server again, a helpful message is now displayed, letting you know that there is already a server trying to start on that port:
[27713] There is already a server trying to start on this port. Please be patient -- this can take a while.
...and the client does not attempt to start a duplicate server for that port.
If you wait long enough, the existing server should be up and ready to play scores. You can check the status of the server by running alda status
.
Made the server a little more resilient to failure. There are a handful of tasks that are handled in parallel, like setting up the audio systems (e.g. MIDI) for a score. This involves running the tasks in parallel background threads and waiting for them to complete. This works fine when the tasks are successful, but if they fail, then the server ends up waiting forever and needs to be restarted in order to serve any more requests.
Starting from this release, we are able to notice when the background tasks fail and re-throw the error so that the server can report the error back to the client and will be ready to handle any subsequent requests.
One example of a background task that can fail is if you try to play an Alda score with multiple MIDI percussion instruments. There is only one MIDI percussion channel available, so this will throw a "Ran out of MIDI channels! :(" error. on the background thread that loads the instruments. Before this release, the server would just lock up when you tried to play such a score; now it will report the error back to the client.
Added more debug logging when running a server in debug mode.
The major change in this release is that we replaced the internal implementation of the server, previously a resource-intensive HTTP server, with a more lightweight ZeroMQ REQ/RES socket. This means lower overhead for the server, which should translate to better performance.
We are using the pure Java implementation of ZeroMQ, which means no native dependencies to install. You can update Alda and it'll just work™.
This is a transparent change; you should not notice any difference in your usage of Alda via the alda
command-line client, besides seeing better performance from the server.
Bugfix: I just realized that the alda play
command's options --from
and --to
were not actually hooked up so that they did anything useful. Oops! Sorry about that. As of this release, you can use them to specify start and end points for playing back the score, either as marker names or minute/second markings. For real, this time.
I discovered that the -y/--yes
auto-confirm option for the alda play
and alda parse
commands was never being used, so I removed the option from both commands.
When I first wrote the Alda client, the play
and parse
commands would overwrite whatever score you were currently working on in memory. So, if you had unsaved changes, the client would warn you that you were about to overwrite the in-memory score and lose your unsaved changes, and ask you for confirmation. The -y
flag was there as a convenience so that if you didn't care about the score in memory and you just wanted to play a score file, you could include the flag and skip the unsaved changes warning and prompt.
A while back, we improved the experience so that the default behavior of alda play
and alda parse
is to play or parse a score separately without affecting the working score you might have in-memory. The client never needs to prompt you for confirmation anymore when using these two commands, so the -y
flag is no longer necessary.
Note: The
-y
flag is still available for other commands that can affect the score in memory, such asalda load
andalda down
.
If you happened to be using the HTTP server directly (i.e. via curl
instead of the Alda client), you will find that this will no longer work, since the server does not respond to HTTP requests anymore.
If you still wish to have lower-level access to an Alda server, you can use ZeroMQ to make a REQ socket connection to
tcp://*:27713
(assuming 27713 is the port on which the Alda server is running), send a request with a JSON payload, and receive the response. For more information about ZeroMQ, read the ZeroMQ guide, it's excellent!
Before, if you ran a command that requires the Alda server to be up, for example alda play -f somefile.alda
, and the Alda server is not up, the client would go ahead and start it for you. We have removed this behavior; now, you will get an error message notifying you that the server is down and explaining that you can start the server by running alda up
. We are working toward making the server failsafe enough that you can leave it running in the background forever and forget about it (making it more of a daemon), so hopefully you will not need to run alda down
or alda up
very often.
When running alda score -m
(to show the data representation of the current score) or alda parse -m -f somefile.alda
(to show the data representation of the score in a file), the output is now JSON instead of EDN.
The reason for this is that JSON is more widely supported and there are a variety of useful command-line tools like jq
for working with JSON on the command line.
For example, if you have jq
installed, you can now run this command to pretty-print an Alda score map:
$ alda score -m | jq .
{
"chord-mode": false,
"current-instruments": [
"flute-nUVei"
],
"score-text": "(tempo! 90)\n(quant! 95)\n\npiano:\n o5 g- > g- g-/f > e- d-4. < b-8 d-2 | c-4 e- d- d- <b-1/>g-\n\nflute:\n r2 g-4 a- b-2. > d-32~ e-16.~8 < b-2 a- g-1\n",
"events": [
{
...
Or you can output the offsets of every event in the score:
$ alda score -m | jq '.events[] .offset'
666.6666870117188
2000.0000610351562
5333.333465576172
1333.3333740234375
1333.3333740234375
4666.666748046875
4750.00008392334
Removed the --pre-buffer
and --post-buffer
options, as I realized they weren't necessary. For details, see this issue comment.
Improved the timing of the behind-the-scenes clean-up that occurs after a score is finished playing.
Fixed a bug where in some cases, that clean-up might not have been happening.
Implemented a minor optimization to the way events are scheduled; earlier events are now given priority scheduling, making it less likely for events to be skipped during playback.
More variable-related bugfixes in this release:
Setting a variable that includes a repeated event sequence, e.g. foo = [c c]*3
no longer throws an error.
Setting a variable that includes an empty Clojure expression, e.g. foo = () c d e f
no longer throws an error.
Shout-out to elyisgreat for finding all these bugs!
Behind the scenes change: simplified the code that handles getting and setting variables. See ebf2a4 for more details.
TL;DR: when you use a variable inside the definition of another variable, now the value of the variable is retrieved right away, instead of waiting until you try to get the value of the outer variable.
For the most part, you should not notice any changes to the way that variables work, aside from the Alda score map (i.e. the result of using alda parse -m
, or :map
in the Alda REPL.) no longer containing the key :variables
and having a greatly simplified :env
key that is no longer a nested map, but a simple lookup of variables to their values.
Trying to define a variable where the value includes an undefined variable now throws an error immediately, instead of waiting until you get the value of the variable. For example, the following score will now fail immediately because bar
isn't defined:
foo = bar
Whereas before this release, the "undefined variable" error would not be thrown until you tried to use foo
in an instrument part.
Minor bugfix: in some situations, undefined variables were not being appropriately caught. Now, an error will always be thrown if you try to use a variable that hasn't been defined.
Empty event sequences, e.g. []
are now supported. Hey, why not? ㄟ( ・ө・ )ㄏ
Defining a previously undefined variable as itself, e.g. foo = foo
used to trigger a stack overflow. Now it throws an "undefined variable: foo" error, which is more helpful.
There was a breaking change in one of the last few versions that may not have been documented, where it is no longer acceptable to place barlines in between notes instead of whitespace, like this:
c|d|e
To improve parsing flexibility, make the parser easier to develop, avoid unexpected bugs in future releases, and enforce a convention that will make Alda scores easier to read, whitespace is now required between all events, including not only notes, chords, event sequences, etc., but also barlines.
As of this release, it is no longer valid to end a note duration with a barline, e.g.:
c1| d
The correct way to write the above is:
c1 | d
The reasoning for this is the same as the point above -- events (including barlines) must be separated by whitespace.
Note that there is one situation that is a minor exception where it is acceptable for a barline to not be preceded/succeeded by whitespace:
c1~|1~|1 d
In this case, there are technically two "events" -- the C note and the D note. Alda's parser reads the C note as a single event where the pitch is C and the duration is 3 whole notes tied across 2 bar lines.
If this is confusing, note that it is also acceptable to put spaces in between the barlines:
c1 | ~1 | ~1 d
The last two examples are equivalent.
Refines the behavior of variables when used within the definitions of other variables. The "scope" of a variable is now tracked when it is defined. This makes it possible to do things like this:
foo = c d e
foo = foo f g
piano: foo # expands to "c d e f g"
It also makes it so that you won't run into unexpected bugs when redefining a variable that another variable depends on, as in this example:
foo = c d e
bar = foo f g
foo = c
piano: bar # still "c d e f g," because that's what it was when it was defined
Prior to this release, trying to use a variable that wasn't defined would result in that variable being ignored and the rest of the score processed as usual.
Now, trying to use a variable that hasn't been defined results in an error being thrown. This will help score writers catch unintentional bugs caused by misspelling variable names, etc.
Fixes another bug related to >
and <
being used back-to-back without spaces in between.
An Alda score containing only Clojure code (i.e. no instrument parts) is now considered a valid score. For example, the following is a valid Alda score:
(part "bassoon"
(for [x (map (comp keyword str) "cdefgab")]
(note (pitch x) (ms 100))))
The previous release inadvertently made it invalid for a note to be followed immediately (no spaces) by an octave up/down operator, e.g. c<
. This release makes it acceptable again to do that.
(It's also still acceptable to follow an octave up/down operator immediately with a note, e.g. >c
, and to sandwich a note between octave up/down operators, e.g. >c<
.)
=
sign is used to define variables, the natural sign, which used to be =
, has been changed to _
to avoid confusion. If you have any scores using naturals, make sure you change the =
's to _
's to avoid parse errors.Variables implemented! This simple, but powerful feature allows you to define named sequences of musical events and refer to them by name. You can even define variables that refer to other variables, giving you the means to build a score out of modular parts. For details, see the docs.
Minor parsing performance improvements.
In order to avoid conflicts between variable names and multiple notes squished together (e.g. "abcdef"), the rules are now more rigid about spaces between notes. Multiple letters back-to-back without spaces in between is now read as a variable name. Multiple letters with spaces between each one (e.g. "a b c d e f") is read as multiple notes.
Note that this does not only apply to single letters, but to anything else that the parser considers a "note" -- this includes notes that include a duration (e.g. "a4"), notes that include multiple durations tied together (e.g. "a4~4~4"), and notes that end in a final tie/slur, indicating that the note is to be played legato (e.g. "a~").
A couple of Alda example scores contained examples that broke the "mandatory space between notes" rule, and had to be changed. For example, awobmolg.alda
contained the following snippet representing 5 notes under a slur:
b4.~b16~a~g~a
The parser was trying to read this as the (legato/slurred) note b4.~
followed immediately by another note, b16
. This is now explicitly not allowed. The example was changed to the following (valid) syntax:
b4.~ b16~ a~ g~ a
Parsing/playing Alda scores is now significantly faster, thanks to some optimizations to the parser. (Many thanks to aengelberg for your help with this!)
Fixed #235 -- when trying to parse (as a --map
) or play a very large score, a "Method code too large!" error was occurring because of the way that scores were parsed into Clojure code as an intermediate form and then eval
'd. Now, the parser transforms an Alda score into the score map (i.e. the output of alda parse --map
) directly.
Even though parsing and playing scores no longer does so by generating Clojure code, it is still possible to generate the Clojure code, if desired, by using alda parse --lisp
.
This should be a transparent change; both ways of parsing should still work the same as before.
Part of the process of optimizing the Alda parser was removing cases of ambiguity. A consequence of doing this is that the duration
grammar rule no longer includes a barline
or slur
at the end. Instead, a barline
must stand on its own (after the note
containing the duration
), and a slur
must be part of a note
instead of its duration
.
In other words, when writing alda.lisp code, whereas it used to be possible to do things like this:
(note (pitch :c)
(duration (note-length 4)
(barline)))
(note (pitch :c)
(duration (note-length 4)
:slur))
Now you can only do it like this:
(note (pitch :c)
(duration (note-length 4)))
(barline)
(note (pitch :c)
(duration (note-length 4))
:slur)
This is a trivial change, but I thought I'd mention it just in case anyone runs into it.
Fixed issue #27. Setting note quantization to 100 or higher no longer causes issues with notes stopping other notes that have the same MIDI note number.
Better-sounding slurs can now be achieved by setting quant to 100:
bassoon: o2 (quant 100) a8 b > c+2.
This release includes numerous improvements to the Alda codebase. The primary goal was to make the code easier to understand and more predictable, which will make it possible to improve Alda and add new features at a much faster pace.
To summarize the changes in programmer-speak: before this release, Alda evaluated a score by storing state in top-level, mutable vars, updating their values as it worked its way through the score. This code has been rewritten from the ground up to adhere much more to the functional programming philosophy. For a better explanation, read below about the breaking changes to the way scores are managed in a Clojure REPL.
Alda score evaluation is now a self-contained process, and an Alda server (or a Clojure program using Alda as a library) can now handle multiple scores at a time without them affecting each other.
Fixed issue #170. There was a 5-second socket timeout, causing the client to return "ERROR Read timed out" if the server took longer than 5 seconds to parse/evaluate the score. In this release, we've removed the timeout, so the client will wait until the server has parsed/evaluated the score and started playing it.
Fixed issue #199. Local (per-instrument) attributes occurring at the same time as global attributes will now override the global attribute for the instrument(s) to which they apply.
Using @markerName
before %markerName
is placed in a score now results in a explicit error, instead of throwing a different error that was difficult to understand. It turns out that this never worked to begin with. I do think it would be nice if it were possible to "forward declare" markers like this, but for the time being, I will leave this as something that (still) doesn't work, but that we could make possible in the future if there is demand for it.
The default behavior of alda play -f score.alda
/ alda play -c 'piano: c d e'
is no longer to append to the current score in memory. Now, running these commands will play the Alda score file or string of code as a one-off score, not considering or affecting the current score in memory in any way. The previous behavior of appending to the current score is still possible via a new alda play
option, -a/--append
.
Creating scores in a Clojure REPL now involves working with immutable data structures instead of mutating top-level dynamic vars. Whereas before, Alda event functions like score
, part
and note
relied on side effects to modify the state of your score environment, now you create a new score via score
(or the slightly lower-level new-score
) and update it using the continue
function. To better illustrate this, this is how you used to do it before:
(score*)
(part* "piano")
(note (pitch :c))
(chord (note (pitch :e)) (note (pitch :g)))
Evaluating each S-expression would modify the top-level score environment. Evaluating (score*)
again (or a full score wrapped in (score ...)
) would blow away whatever score-in-progress you may have been working on.
Here are a few different ways you can do this now:
; a complete score, as a single S-expression
(def my-score
(score
(part "piano"
(note (pitch :c))
(chord
(note (pitch :e))
(note (pitch :g))))))
; start a new score and continue it
; note that the original (empty) score is not modified
(def my-score (new-score))
(def my-score-cont
(continue my-score
(part "piano"
(note (pitch :c)))))
(def my-score-cont-cont
(continue my-score-cont
(chord
(note (pitch :e))
(note (pitch :g)))))
; store your score in an atom and update it atomically
(def my-score (atom (score)))
(continue! my-score
(part "piano"
(note (pitch :c))))
(continue! my-score
(chord
(note (pitch :e))
(note (pitch :g))))
Because no shared state is being stored in top-level vars, multiple scores can now exist side-by-side in a single Alda process or Clojure REPL.
Top-level score evaluation context vars like *instruments*
and *events*
no longer exist. If you were previously relying on inspecting that data, everything has now moved into keys like :instruments
and :events
on each separate score map.
(duration <number>)
no longer works as a way of manually setting the duration. To do this, use (set-duration <number>)
, where <number>
is a number of beats.
The $
syntax in alda.lisp (e.g. ($volume)
) for getting the current value of an attribute for the current instrument is no longer supported due to the way the code has been rewritten. We could probably find a way to add this feature back if there is a demand for it, but its use case is probably pretty obscure.
Because Alda event functions no longer work via side effects, inline Clojure code works a bit differently. Basically, you'll just write code that returns one or more Alda events, instead of code that produces side effects (modifying the score) and returns nil. See entropy.alda for an example of the way inline Clojure code works starting with this release.
Command-specific help text is now available when using the Alda command-line client. (jgerman)
To see a description of a command and its options, run the command with the -h
or --help
option.
Example:
$ alda play --help
Evaluate and play Alda code
Usage: play [options]
Options:
-c, --code
Supply Alda code as a string
-f, --file
Read Alda code from a file
-F, --from
A time marking or marker from which to start playback
-r, --replace
Replace the existing score with new code
Default: false
-T, --to
A time marking or marker at which to end playback
-y, --yes
Auto-respond 'y' to confirm e.g. score replacement
Default: false
b>/d/f
(#192 - thanks to elyisgreat for reporting!)You can now update to the latest version of Alda from the command line by running alda update
. (jgkamat)
This will be the last update you have to install manually :)
midi-percussion
instrument. See the docs for more info.alda new
now asks you for confirmation if there are unsaved changes to the score you're about to delete in order to start one.alda info
prints useful information about a running Alda serveralda list
(currently Mac/Linux only) lists Alda servers currently running on your systemalda load
loads a score from a file (without playing it)
alda save
saves the current score to a file
alda new
will call this function implicitly if you give it a filename, e.g. alda new -f my-new-score.alda
alda edit
opens the current score file in your $EDITOR
alda edit -e <editor-command-here>
opens the score in a different editoralda repl
. This is just as slow to start as it was before, as it still has to start the Clojure run-time, load the MIDI system and initialize a score when you start the REPL. In the near future, however, the Alda REPL will be much more lightweight, as it will be re-implemented in Java, and instead of starting an Alda server every time you use it, you'll be interacting with Alda servers you already have running.boot build -o directory_name
will generate alda.jar
, alda
, and alda.exe
files which can be run directly.bin/alda
Boot script that we were previously using as an entrypoint to the application is no longer needed, and has been removed.scripts/install-fluid-r3
). Running it will download FluidR3 and replace ~/.gervill/soundbank-emg.sf2
(your JVM's default soundfont) with it. (If you're a Windows user and you know how to install a MIDI soundfont on a Windows system, please let us know!)alda parse
task.Improved parsing performance, especially noticeable for larger scores. More information here, but the TL;DR version is that we now parse each instrument part individually using separate parsers, and we also make an initial pass of the entire score to strip out comments. This should not be a breaking change; you may notice that it takes less time to parse large scores.
As a consequence of the above, there is no longer a single parse tree for an entire score, which means parsing errors are less informative and potentially more difficult to understand. We're viewing this as a worthwhile trade-off for the benefits of improved performance and better flexibility in parsing as Alda's syntax grows more complex.
Minor note that will not affect most users: alda.parser/parse-input
no longer returns an Instaparse failure object when given invalid Alda code, but instead throws an exception with the Instaparse failure output as a message.
Custom events can now be scheduled via inline Clojure code.
Added electric-bass
alias for midi-electric-bass-finger
.
Note lengths can now be optionally specified in seconds (c2s
) or milliseconds (c2000ms
).
Repeats implemented.
:quit
to the list of commands available when you type :help
.:help
to see all available commands, or :help <command>
for additional information about a command.!
characters like bash does. (#125)Implemented code block literals, which don't do anything yet, but will pave the way for features like repeats.
alda-code
function added to the alda.lisp
namespace, for use in inline Clojure code. This function takes a string of Alda code, parses and evaluates it in the context of the current score. This is useful because it allows you to build up a string of Alda code programmatically via Clojure, then evaluate it as if it were written in the score to begin with! More info on this in the docs.
Bugfix (#120), don't allow negative note lengths.
Handy alda script
task allows you to print the latest alda script to STDOUT, so you can pipe it to wherever you keep it on your $PATH
, e.g. alda script > /usr/local/bin/alda
.
from
/to
options where playback would always start at offset 0, instead of whenever the first note in the playback slice comes in.from
and to
arguments allow you to play from/to certain time markings (e.g. 1:02 for 1 minute, 2 seconds in) or markers. This works both from the command-line (alda play --from 0:02 --to myMarker
) and in the Alda REPL (:play from 0:02 to myMarker
). (crisptrutski)
Simplify inline Clojure expressions -- now they're just like regular Clojure expressions. No monkey business around splitting on commas and semicolons.
The alda
script has changed in order to pave the way for better/simpler inline Clojure code evaluation. This breaks attribute-setting if you're using an alda
script from before 0.10.0. You will need to reinstall the latest script to /usr/local/bin
or wherever you keep it on your $PATH
.
This breaks backwards compatibility with "multiple attribute changes," i.e.:
(volume 50, tempo 100)
This will now attempt to be read as a Clojure expression (volume 50 tempo 100)
(since commas are whitespace in Clojure), which will fail because the volume
function expects only one argument.
To update your scores that contain this syntax, change the above to:
(do (volume 50) (tempo 100))
or just:
(volume 50) (tempo 100)
panning
attribute.Added the ability to specify a key signature via the key-signature
attribute. Accidentals can be left off of notes if they are in the key signature. See the docs for more info on how to use key signatures. (FragLegs/daveyarwood)
=
after a note is now parsed as a natural, e.g. b=
is a B natural. This can be used to override the key signature, as in traditional music notation.
alda.lisp
namespace.
To preserve backwards compatibility, attributes still work the same way -- they just happen to be function calls now -- and there is a special reader behavior that will split an S-expression into multiple S-expressions if there is a comma or semicolon, so that there is even backwards compatibility with things like this: (volume 50, tempo! 90)
(under the hood, this is read by the Clojure compiler as (do (volume 50) (tempo! 90))
).(* long comment syntax *)
. This syntax will now be interpreted as a Clojure S-expression, which will fail because it will try to interpret everything inside as Clojure values and multiply them all together :) The "official" way to do long comments in an Alda score now is to via Clojure's comment
macro, or you can always just use short comments.:play
command was only resetting the current/last offset of all the instruments for playback, causing inconsistent playback with respect to other things like volume and octave. Now it resets all of the instruments' attributes to their initial values, so it is truly like they are starting over from the beginning of the score.Fixed another regression caused by 0.6.1 -- tying notes across barlines was no longer working because the barlines were evaluating to nil
and throwing a wrench in duration calculation.
Added a --tree
flag to the alda parse
task, which prints the intermediate parse tree before being transformed to alda.lisp code.
barline
function in alda.lisp.events.barline
wasn't actually being loaded into alda.lisp
. Also, add debug log that this namespace was loaded into alda.lisp
.:play
command -- plays the current score from the beginning. (crisptrutski/daveyarwood)Exit with error code 1 when parsing fails for alda play
and alda parse
tasks. (MadcapJake)
alda play
task.octave-set
, octave-up
and octave-down
tokens instead of one catch-all octave-change
token. (crisptrutski)alda parse
task.:load
loads a score from a file.:map
prints the current score (as a Clojure map of data).:score
prints the current score (Alda code).TIMBRE_LEVEL
environment variable.update
command.:score
REPL command which will print the score whenever you want.help
and version
tasks moved to the top of help textalda help
commandalda
executable scriptalda.version
#{ this }
to (* this *)
.alda play
task now reports parse errors.alda.sound/play!
now automatically determines the audio types needed for a score, making alda.sound/set-up! <type>
optional.
various internal improvements / refactorings
track-volume
attribute was not being included in notes due to a typo.Can you improve this documentation? These fine people already did:
Dave Yarwood, Dave Yarwood and elyisgreat, Jared Beckham & Chris TruterEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close