-
The volume change messages that end up in the MIDI sequences generated by Alda (the values come from the Alda
track-volume
attribute) used to be Channel Volume (7) messages, but are now Expression (11) messages.As truj pointed out in alda-lang/alda-core#75, Expression (11) is a more appropriate message to use in a MIDI sequence, whereas Channel Volume (7) is intended to be used for mixing purposes during playback, such as by his program, Midica.
Thanks for pointing this out, truj!
-
Fixed a bug where attempting to play an empty score (e.g.
c d e
, which results in an empty score because there are no instruments) results in a NullPointerException.The problem was that there were no instruments, so the MIDI system wasn't being set up. However, we expect the MIDI system to be set up even if there are no instruments and no notes to play, because we use the MIDI Sequencer to schedule notes.
- Fixed a JDK 9+ illegal access warning that happens when calling
get-midi-sequencer!
.
- Fixed a JDK 9+ illegal access warning that happens when calling
all-sound-off!
.
-
alda.sound
now has anexport!
function, which takes a score and an output filename and exports the score as a MIDI file. -
When a score is played or exported, the MIDI sequence division type and resolution are now PPQ and 128, whereas they were SMPTE and 2 before. Using PPQ allows us to export MIDI files that are much more useful when imported into other music software such as sheet music editors.
-
(BREAKING CHANGE) Removed the
alda.now
namespace. Alda no longer depends on it internally, and it is no longer the recommended way to do live-coding in Alda.The new recommended way for Clojure programmers to live code music with Alda is alda-clj.
-
Major overhaul of the event scheduling system, which used to leverage JSyn as a scheduler. We now play scores by creating Java MIDI Sequences and playing them via a Sequencer and Synthesizer.
-
BREAKING CHANGES:
-
Removed public vars from
alda.sound
:*synthesis-engine*
,new-synthesis-engine
,start-synthesis-engine!
,refresh!
,refresh-audio-type!
,start-event!
,stop-event!
,schedule-event!
,schedule-events!
-
Removed
:synthesis-engine
from the audio context. -
Removed the
start-event!
andstop-event!
lifecycle events, as this functionality is now handled by the sequencer. -
Removed the
refresh!
lifecycle event, in the interest of trying to keep things idempotent. The idea is that we don't want to tolerate having to "reset" an audio context between playing scores. Prior to this release, we were usingrefresh!
to re-load the MIDI instruments of the score into the synthesizer. Now, we do this by making the program change events part of the MIDI sequences we create. -
Removed public vars from
alda.sound.midi
:load-instruments!
,protection-key-for
,protect-note!
,unprotect-note!
,note-reserved?
,play-note!
,stop-note!
-
alda.sound.midi/load-instruments!
replaced by a more granular function calledmap-instruments-to-channels!
-
Real-time playback functionality in
alda.sound.midi
(play-note!
,stop-note!
) has been removed; the sequencer handles all of that now.
-
-
Non-breaking changes:
-
Added
:midi-sequencer
to the audio context. -
Usage of
alda.sound/schedule-events!
inalda.sound.play!
has been replaced by usage of new functions,alda.sound/create-sequence!
andalda.sound.midi/play-sequence!
.
-
- Removed stale references to scheduled functions, for compatibility with alda/core 0.4.0.
-
Fixed a bug where
:from
playback option was being ignored when anevent-set
argument was included toplay!
. -
Added some debug logging around
*play-opts*
and playback start/end offsets.
-
Fixed a bug in the MIDI
all-sound-off!
function where not every channel was necessarily being stopped because of the laziness ofpmap
. Adding in adoall
forces evaluation of the "stop sound" function on every channel.Also, added in a call to
MidiChannel.allNotesOff()
in addition to theMidiChannel.allSoundOff()
that we already had, to be extra sure that all sound will stop.
-
There is now a distinction between
tear-down!
andstop-playback!
.stop-playback!
can be used in contexts where you want to stop playback of a score and you might want to start playing the score again after that. It stops playback, but leaves the audio context in a state where it can continue to play. -
(BREAKING CHANGES) Cleaned up a bunch of code. In particular, a lot of functions in
alda.sound
that took anaudio-ctx
and optionally a score now just take a score that contains an:audio-context
. This is a simpler API. -
(BREAKING CHANGE) The score returned by
alda.sound/play!
is now guaranteed to contain an:audio-context
. If there isn't one on the score that's passed in, a new audio context is generated andassoc
'd onto the score.
-
(BREAKING CHANGE)
alda.sound/play!
now returns a map containing multiple values::score The full score being played.
:stop! A function that, when called mid-playback, will stop any further events from playing.
:wait A function that will sleep for the duration of the score. This is useful if you want to playback asynchronously, perform some actions, then wait until playback is complete before proceeding.
...whereas before, it returned just the
stop!
function. -
Added a
with-score*
macro toalda.now
. It behaves just likewith-score
, but returns the last form of itsbody
instead of the score. -
(BREAKING CHANGE)
alda.now/play-with-opts!
now returns thealda.sound/play!
result. -
Fixed a bug where a score didn't technically "end" when it was stopped. By that, I mean that the Clojure promise was never being delivered the
:done
value, and in some cases, playing a score synchronously could result in blocking forever. This is fixed now.
- Fixed a minor bug re: interaction between using
play!
with anevent-set
and adjusting start/end bounds via thefrom
andto
options.
- Added a
play-with-opts!
function that is likeplay!
, but allows the caller to override the playback options used when playing the score.