Score Mapping
Like Common Music and SmOKe, Score’s internal design is backend agnostic. This means that the representation of data is not tied to a single target music system. Users can use mapping functions to convert note lists generated by Score into a format that works with another system. This may be for use with other computer music systems but may also be used for visualisation or other purposes.
The following shows an example use of Score and Csound. It uses the gen-notes2
function to generate a note list from time 0 to 5.0 using 5 fields. The first field is a constant field that will always generate 1. The rest of the p-fields of the Csound score is generated using the values provided by within the score.mask
package.
(def notes
(gen-notes2 0 5.0
1
(gauss 0.5 0.1)
(heap [0.1 0.2 0.4])
(rand-range 0.1 0.25)
(rand-item
["8.00" "8.03" "8.02"])))
(def csound-sco
(format-sco notes))
(println notes)
(println csound-sco)
The results of printing the output are shown below:
;; output from (println notes)
[[1 0.0 0.1 0.1455446063675899 8.02]
[1 0.07388877495229043 0.2 0.11487888605849467 8.00]
[1 0.2684591839186033 0.4 0.12170487899979296 8.00]
[1 1.0558572506209922 0.4 0.13304255988624555 8.03]
[1 1.554791683668857 0.2 0.16436113185377213 8.00]
[1 1.9392915161730429 0.1 0.11907587313489418 8.02]
[1 2.3410899943560195 0.2 0.21996317376289015 8.03]
[1 2.787924993057282 0.4 0.2119026696996974 8.00]
[1 3.7580 770774079575 0.1 0.12327608647786711 8.00]
[1 4.199933807980773 0.2 0.23620482696864334 8.00]]
;; output from (println csound-sco)
i1 0.0 0.1 0.1455446063675899 8.02
i1 0.07388877495229043 0.2 0.11487888605849467 8.00
i1 0.2684591839186033 0.4 0.12170487899979296 8.00
i1 1.0558572506209922 0.4 0.13304255988624555 8.03
i1 1.554791683668857 0.2 0.16436113185377213 8.00
i1 1.9392915161730429 0.1 0.11907587313489418 8.02
i1 2.3410899943560195 0.2 0.21996317376289015 8.03
i1 2.787924993057282 0.4 0.2119026696996974 8.00
i1 3.7580770774079575 0.1 0.12327608647786711 8.00
i1 4.199933807980773 0.2 0.23620482696864334 8.00
The first printout shows the results of running gen-notes2
, which produces a Clojure list of lists. The second printout shows the result of using the format-sco
function, provided by Score for formatting note lists into Csound SCO text format. The csound-sco
text may then be further sent to a running Csound instance for live score performance or written to disk and later read by Csound as a SCO file.
At this time, Score only provides output mapping for Csound. However, Score’s generated note lists are usable as-is with Pink, as both systems are written in Clojure. In track1.clj, growing-line
defines a note list using both features from Score and Pink. The code first uses two note lists generated using the gen-notes
function that are concatenated together. This is then mapped over and the growl
audio function is prepended as the first field of each note in the note list. The e
argument given to gen-notes
is itself a Pink audio function – the env
function – that is wrapped using the !*!
operator. The result is that for each note, the 6th field will be an instance of env
used as the amplitude argument to the growl
instrument.
;; from music-examples.track1 example file
(def growing-line
(let [e (!*! env [0.0 400 0.11 5000])
starts (range 0 1.8 (/ 1.0 3.0))
amps (range 0.05 5 0.05)
space (range 0.75 -1.0 -0.25)]
(map #(into [growl] %)
(concat
(gen-notes starts 0.1 :G5 amps e 0.75 space)
(gen-notes starts 0.1 :G3 amps e 0.75 space)
))))
From here, the growing-line
note list is then reused as a part of a larger measured-score. convert-measured-score
is used to prodcue to the total score, which is then mapped into Pink events using the sco->events
function provided in the pink.simple
namespace.
(defn apply-afunc-with-dur
"Applies an afunc to given args within the context of a
given duration. with-duration will bind the value of dur
to the *duration* Pink context variable."
[afunc dur & args]
(with-duration (double dur)
(apply!*! afunc args)))
(defn i
"Csound style note events: audio-func, start, dur, & args."
[afunc start dur & args]
(apply event apply-afunc-with-dur start afunc dur args))
(defn sco->events
"Converts Csound-style note list into a list of
Pink Events."
[notes]
(map #(apply i %) notes))
The above shows the code for sco->events
. Given a list of notes, sco->event
maps an anonymous function that applies the i
function to the values found in each note. The i
function in turn applies the event
function to each note, using apply-afunc-with-dur
as the event’s function – the one that will fired by Pink’s event processor – with the given arguments. Finally, when apply-afunc-with-dur
is called, it fires by processing the values found in the original note, applying the first field – the audio function – to the rest of the fields.
In the full track1.clj example, these Pink events are further passed to the add-audio-events
function from pink.simple
. This is a convenience function that wraps events with another event that uses the add-afunc
function to attach audio functions to the root node of the engine. At runtime, when an event is fired, the nested event will generate an audio function and the top-level event will add it to the engine for processing.
The mapping of note lists is the technique by which the generated data from Score is connected to other systems. Score currently provides a mapping function for Csound and works out of the box with Pink, as shown in the example code. In the future, more mappings could be provided with Score, such as MIDI, OSC, and MusicXML. As the data generated from Score is plain Clojure list data, users can create their own mappings relatively simply.