Relevant files:
Main.kt
alda-player
is a simple command-line program that runs an Alda player process.
We use the Clikt library to handle command/option/argument parsing.
Commands (e.g. alda-player run
, alda-player info
) are implemented as
subclasses of the CliktCommand
class.
Relevant files:
log4j2.xml
,Main.kt
We use the lightweight kotlin-logging library for logging. Before we
initialize the logger, we set a couple of system properties, logPath
and
playerId
. These are used in the logging config to control the location of the
log file and the ID of the player process, which is included at the beginning of
every log line. (These are the same player IDs that you see when you run alda ps
to list running player processes).
The location of the log files is based on platform-specific conventions (e.g. the XDG specification for Linux), as codified in the directories-jvm library.
See the
cacheDir
row in this table for reference about where to find the logs on your system. (Logs are also printed to stdout when you're running the player process, which is usually good enough!)On my Ubuntu 16.04 machine, the current log file is
/home/dave/.cache/alda/logs/alda-player.log
. Older log files are found in the same directory, with names likealda-player-2021-02-10.log
.
Relevant files:
StateManager.kt
It is important that the Alda client can easily discover which player processes are available to play a score. When a player process comes up, it creates a StateManager instance, which runs a background thread that continuously updates a state file in the cache directory.
See the
cacheDir
row in this table if you need help locating the player state files on your system.On my Ubuntu 16.04 machine, the player state files are found in
/home/dave/.cache/alda/state/players/1.99.3/
(where1.99.3
is the current Alda version, controlled by theVERSION
resource file in this repo).
Here is some example state file content:
{"expiry" : 1613251867419, "port" : 36841, "state" : "ready"}
In order to discover available player processes, the Alda client looks in the
player state file directory for any state files that show the player's state is
ready
.
A player process begins in the starting
state. After initializing, its state
becomes ready
. Once a player process starts receiving messages from a client,
it becomes active
.
A player process shuts down after a random length of inactivity between 5 and 10
minutes. This helps to ensure that a bunch of old player processes aren't left
hanging around, running idle in the background. The time at which a player
process is set to expire is represented in the state file as expiry
, as a Unix
timestamp (i.e. time since the Unix epoch) in milliseconds.
Relevant files:
Receiver.kt
,Parser.kt
The Alda client sends messages to the Alda player using the Open Sound Control protocol over TCP. The Alda OSC API is documented here.
Incoming OSC messages/bundles are placed onto a queue and parsed and processed in the order that they were received.
Relevant files:
MidiEngine.kt
Although the Java MIDI synthesizer is currently Alda's only method of producing audio, there are plans to (eventually) include other types of audio instruments, such as waveform synthesizers and audio samplers.
In the future, when we have instruments like those, we will likely still use the Java MIDI sequencer to manage the scheduling and sychronization of musical events, the same way we are doing now. This is possible by using MetaMessages to schedule arbitrary events in a MIDI sequence.
We are already using MetaMessages for a handful of things. For example, to support live coding, we regularly schedule "continue" metamessages that we handle by making sure that the sequencer is still running once it reaches the end of the current sequence.
Another thing we use MetaMessages for is deferring the scheduling of events in a pattern until right before the pattern is due to be played. This is useful because patterns are mutable, which allows composers to do fun things like loop a pattern and change the notes in the pattern live during a performance.