alda-lang/alda

Modules / multiple file scores

Closed this issue · 4 comments

Moved from #133.

The "module" idea is essentially to take the variable concept and apply it to entire files.

For example, each instrument's part could be written in a separate file in the same directory as a main file (perhaps even organized into subdirectories), then in the main file (say, "score.alda"), you could write something like:

(load-module "woodwinds/clarinet.alda")
(load-module "brass/trombone.alda")
(load-module "brass/tuba.alda")
(load-module "strings/banjo.alda")
(load-module "piano.alda")
(load-module "/path/to/drums.alda")

and each of these statements would be replaced by the contents of each file at compile time.

This would be especially cool in that you could define custom instruments (e.g. custom synth instruments) as standalone files and then load them in as modules in any piece of music in which you'd like to use them.

Interestingly, since inline Clojure code evaluation was added, it has been possible to load Clojure code from other files (via clojure.core/load-file), which is great in itself and gets us halfway there. load-module could read Alda (and by extension, Clojure) code from a file and parse and evaluate it within the context of the current score. Instrument parts and even rudimentary plugins could thus be written and distributed as .alda files.

An open question I have is how load-file and load-module will handle relative paths. Ideally, it will be possible to distribute Alda scores as folders containing multiple .alda files, as git repos, etc. So, relative paths should ideally be interpreted relative to the score they're written in, not the user's current working directory. (This might already work by default when you use clojure.core/load-file.)

Is this already implemented?

I tried with:

(load-module "lib.alda")
piano: V1: a b c d e f g

And I got:

libre-node:my-songs xmunch$ alda play -f armonizando.alda
[27713] Parsing/evaluating...
[27713] ERROR java.lang.RuntimeException: Unable to resolve symbol: load-module in this context, compiling:(NO_SOURCE_PATH:0:0)

@dgrmunch this feature isn't implemented yet (I think), this is more of an idea which might eventually be integrated into alda!

@daveyarwood I think that this might be pretty easy to implement, if I understand how the context works correctly (I might be able to take a stab at it next week(end) if it is this easy).

Couldn't we just read the file, and pass the contents of the file to a call to (alda-code). Code written in this way seems to get inerpreted in scope properly, for example:

piano: c d e

(alda-code "d e f")

seems to play all the notes. So for example, shouldnt something like:

(defun load-module [filename]
  (alda-code (read filename))

work when placed in code.clj?

This might be broken though, for example, the server will now need access to the file, instead of being sent by the client, in which case we would have to have the client parse and resolve this (in order to keep everything working if a server is on another machine).

Couldn't we just read the file, and pass the contents of the file to a call to (alda-code). Code written in this way seems to get inerpreted in scope properly, for example:

piano: c d e

(alda-code "d e f")

seems to play all the notes. So for example, shouldnt something like:

(defun load-module [filename]
 (alda-code (read filename))

work when placed in code.clj?

Correct -- this is exactly what I had in mind.

You make an interesting point about the server needing access to files, though. Ideally, this shouldn't be the case. But we also don't want the client to need to do any score processing -- to do so, we would have to load the Clojure code on the client side, which would totally defeat the purpose of having a (fast) client and a (slow to start, but then fast to run) server.

I wonder if it would make sense for this to be a special directive at the top of the file that the client can parse, something like:

module woodwinds/clarinet.alda
module brass/trombone.alda
module brass/tuba.alda
module strings/banjo.alda
module piano.alda
module /path/to/drums.alda

It should be very fast for the client to scan the top lines of an Alda file for lines that start with plugin (see alda-lang/alda-core#2) or module, stopping once it encounters the first line that doesn't start with one of those directives. Then, the client can read the files and piece together the score before sending it to the server. I think this would be an acceptable "first cut" of this feature.

After that, we would need to address the issue of the server knowing about files and line numbers, which will take a bit more thought.

In Alda 2.0, I'm planning to move most of the heavy lifting into the client, which should make it trivial to implement this sort of functionality.