Fixed a minor bug related to alternate endings / repetitions, where input like the following would fail to parse correctly:
[[c] [d]'1]*1
The bug had to do with using event sequences in combination with the alternate endings feature.
Fixed a minor bug where the parser would fail to recognize that a note at the
end of a part that ends with a ~
followed by a |
is supposed to be
slurred.
In other words, it was treating c4~ |
at the end of an instrument part as an
un-slurred note, when it's supposed to be slurred.
Fixed buggy error handling logic in the case of an unhandled exception. Before, we were inadvertently hiding the exception and the message ended up being "null." Now the exception message gets through.
midi-note
, is available as an alternative to
pitch
that is occasionally useful for algorithmic compositions, etc. For
example, instead of (note (pitch :c :sharp))
, you can specify the MIDI note
number, (note (midi-note 61))
.Instrument parts now track (local) tempo attribute changes. Instrument parts
now have :tempo/values
, a map of offset (ms) to tempo (bpm).
Added the notion of :tempo/role
to instrument parts. The first instrument
added to a score has the :tempo/role
:master
.
Added a top-level :tempo/values
(also a map of offset (ms) to tempo (bpm))
to the score. These values are a merger of {0 120}
(a default initial tempo
of 120 bpm), the :tempo/values
of the part whose :tempo/role
is :master
,
and any global tempo changes.
alda.lisp.score/new-score
public.Rewrote alda.lisp.instruments.midi
(the definitions of all the MIDI
instruments that Alda knows about) in a more data-oriented way.
This will allow us to add an alda instruments
command (and corresponding
:instruments
Alda REPL command) that can list out the instruments available.
New feature: alternate phrases during iterations of repeats.
This will be documented soon in alda-lang/alda, but for now, see #17 for more information.
Fixed a minor bug in the parser: there was an edge case where a "get variable" event wasn't being disambiguated from its earlier, less-specific "name" form if the "get variable" event happened to be the last thing in the definition of another variable. (#64)
Thanks to elyisgreat for spotting the bug!
Fixed a minor bug where parsing an invalid score like piano: undefinedVariable
would return nil
instead of throwing the error.
The bug was that when a score is syntactically valid but throws an exception while trying to build the score (in the case of this example, because the referenced variable is undefined), the exception is thrown inside a core.async channel and does not affect the main thread -- essentially it gets swallowed, which is a known caveat of exceptions in core.async.
Now, any errors thrown while building the score are passed through the parsing pipeline so that they can be thrown when we're ready to return a result (or throw an exception).
Fixed a bug where the parser did not correctly parse nested events in some situations, for example a set-variable expression containing a CRAM expression containing a chord. (#55)
Thanks to elyisgreat for reporting this issue!
Fixed a bug in the way the program path is determined when a server starts
workers. (That code lives in alda.util, in this repo.) The bug was showing
itself when the path to the alda
(or alda.exe
) executable contained spaces
or other special characters.
Thanks to Hemaolle for the detective work and PR to fix this issue!
Added a reference-pitch
(alias: tuning-constant
) attribute, which will
have an affect on the pitch of each note in Hz. This number is the desired
pitch of A4 (the note A in the 4th octave). The default value is 440 Hz.
However, please note that this value is not currently used. We are still figuring out how to tune MIDI notes in Java -- it is more difficult that one might expect. If you're interested in helping with this, please let us know!
Added a transposition
(alias: transpose
) attribute, which moves all notes
(either per-instrument, or globally, depending on whether you are using
transpose
or transpose!
) up or down by a desired number of semitones.
Positive numbers represent increasing semitones, and negative numbers
represent decreasing semitones.
This attribute can be used to make writing parts for transposing
instruments more
convenient. To see transpose
in use, see this example
score,
a transcription of a saxophone quartet by Juan Santiago Jiménez.
Saxophones are transposing instruments; soprano and tenor saxophones are considered "Bb" instruments, and alto and baritone saxophones are considered "Eb" instruments. This means that an instrument part written for a baritone saxophone, for example, might appear to be written in C major, but when read and performed by a baritone saxophonist, it will sound like Eb major, the intended key.
Thanks, pzxwang, for implementing these new features!
Minor improvement to the new tempo
function overload and metric-modulation
function: the supplied note-length can be a string representing multiple note
lengths tied together, e.g.:
(tempo "4~16" 120)
Thanks to elyisgreat for the issue and pzxwang for the pull request!
Added an overload of tempo
that allows you to specify the tempo in terms of
a note value other than (the default) a quarter note.
For example, "♩. = 150" can be expressed as:
(tempo! "4." 150)
(NB: the note value can be either a number or a string containing a number followed by dots.)
It is still OK to leave out the note value; the default behavior is to set the tempo relative to a quarter note. "♩ = 60" can still be expressed as:
(tempo! 60)
Added a new function, metric-modulation
, which sets the tempo based on a
metric modulation, i.e.
shifting from one meter to another.
Say, for example, that you're writing a score that starts in 9/8 -- three beats per measure, where each beat is a dotted quarter note.
At a certain point in the piece, you want to transition into a 3/2 section -- still three beats per measure, but now each beat is a half note. You want the "pulse" to stay the same, but now each beat is subdivided into 4 half notes instead of 3. How do you do it?
In traditional notation, it is common to see annotations like "♩. = 𝅗𝅥 " at the moment in the score where the time signature changes. This signifies that at that moment, the pulse stays the same, but the amount of time that used to represent a dotted quarter note now represents a half note. When the orchestra arrives at this point in the score, the conductor continues to conduct at the same "speed," but each musician mentally adjusts his/her perception of how to read his/her part, mentally subdividing each beat into 4 eighth notes instead of 3 eighth notes.
In Alda, you can now express a metric modulation like "♩. = 𝅗𝅥 " as:
(metric-modulation! "4." 2)
Thanks, pzxwang, for the PR to add these new features!
Added the following modes:
:ionian
:dorian
:phrygian
:lydian
:mixolydian
:aeolian
:locrian
These can be used as an alternative to :major
and :minor
when specifying a
key signature.
For example:
piano:
(key-sig [:d :locrian])
d8 e f g a b > c d8~1
Thanks, iggar, for this contribution!
Fixed a parser bug where a rest r
followed by a newline inside of a variable
definition would not be considered part of the variable definition.
Thanks, elyisgreat, for reporting this issue!
Thanks, pzxwang for contributing the changes in this release in PR #50!
Non-integer decimal note lengths are now accepted. For example, c0.5
(or a
double whole note, in Western classical notation) is twice the length of c1
(a whole note).
Added a convenient set-note-length
function to alda.lisp.
This is an alternative to set-duration
, which, somewhat unintuitively, sets
the current duration to its argument as a number of beats.
To set the note length to a quarter note (1 beat), for example, you can now
use either (set-duration 1)
or (set-note-length 4)
.
Fixed error handling in parse-input
when parsing in :score
mode (which is
the default). I overlooked the fact that core-async go-loop
doesn't play
nice with error handling, so you have to do something like send the error on
the channel and then throw it outside of the go-loop
.
Before this fix, if an error occurred, the server would attempt to send the exception object itself as a success response and then fail because the exception is not serializable as JSON.
Now, with parse-input
properly throwing exceptions, the server will send an
error response if one is thrown.
r
followed by e.g. ]
would trigger a parser error.Fixed a handful of bugs in the new parser implementation where a one-line variable definition, e.g.:
foo = d8 e f+ g a b4.
...might fail to parse if it ends with certain events.
Re-implemented the parser from the ground up in a more efficient way. The new parser implementation uses core.async channels to complete the stages of the parsing pipeline in parallel.
Performance is roughly the same (only slightly better) for scores under ~100 lines, but significantly better for larger scores.
More importantly, parsing asynchronously opens the door for us to make playing a score happen almost immediately in the near future.
See #37 for more details.
An added benefit of the new parser implementation is that it fixes issue #12. Line and column numbers are now correct, and error messages are more informative when a score fails to parse.
The alda.parser-util
namespace, which included the parse-to-*-with-context
functions, has been removed. See this commit for more details.
The Alda parser no longer generates alda.lisp code.
Originally, the Alda parser created a score by generating alda.lisp code and then evaluating it. This actually changed some time ago to a system where the parser generated a sequence of events directly and then used them to build the score. We kept the code that generates alda.lisp code, even though it was no longer an implementation detail of the parser, just an alternate "mode" of parsing.
With these changes to the parser, it would take some additional work to generate alda.lisp code. Since it is no longer necessary to do that, generating alda.lisp code is no longer a feature of Alda. We could re-implement this feature in the future as part of the new parser, if there is a demand for it.
Miscellaneous implementation changes that could be relevant if you use Alda as a Clojure library:
alda.parser/parse-input
returns a score map, rather than an unevaluated
S-expression. Calling this function will require and refer alda.lisp
for
you if you haven't already done so in the namespace where you're using it.
alda.lisp/alda-code
does not throw an exception by itself if the code is
not valid Alda; instead, the output contains an Exception object, which gets
thrown when used inside of a score
Whereas alda.lisp/pitch
used to return a function to be applied to the
current octave and key signature, now it returns a map that includes its
:letter
and :accidentals
. This is more consistent with other alda.lisp
functions, and it allows notes to have equality semantics.
In other words, whereas (= (note (pitch :c)) (note (pitch :c)))
used to be
false
, now it is true
because we aren't comparing anonymous functions.
(alda.lisp/barline)
now returns {:event-type :barline}
instead of nil
.
Fixed #27, a bug where, when using note durations specified in seconds/milliseconds, the subsequent "default" note duration was not being set.
Thanks to damiendevienne for reporting this bug!
voices
(voice group) event, as bbqbaron and I figured out that it's not necessary. It turns out that each voice
event manages its voice group implicitly. For more discussion, see alda-lang/alda#286.Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close