Function summary | |
---|---|
alternate-fenvs | ids ns fenv-lists &key (interpolation :steps) |
alternate-omn-fenvs | ids no-of-sublists omn-fenv-lists parameters sequence &key (keep-articulations? t) (interpolation :steps) (hairpins? nil) |
alternate-omns | ids seqs-of-seqs &key (append? nil) |
alternate-scores | ids seqs-of-scores |
alternate-subseq-fenvs | ids no-of-sublists fenv-lists parameter sequence &key (min-vel 'pp) (max-vel 'ff) (articulation-maps nil) (keep-articulations? t) (interpolation :steps) (hairpins? nil) |
condense | sequence test &key (section nil) |
developing-substitute | map sequence otherwise |
fenv->omn-parameter | fenv parameter n &key arts-set |
omn->fenv | parameter sequence &key (type :steps) (x-values :rhythm) |
omn->fenvs | sequence &key (parameters '(:length :pitch :velocity)) (type :steps) (x-values :rhythm) |
rotate-omn | n sequence &key (parameter :pitch) (flat t) (section nil) |
section-id-seq | pairs |
vary-motif | motif fn argss |
_alternate-omns-aux | ids hash append? |
_counters-to-1s | xs |
_map-sublist-subseqs | no-of-sublists sequence fn |
Returns a transformation of `sequence', where elements that match entries given in `map' are replaced with given new items, and -- if not contained in map -- with an `otherwise' element. However, this function allows for replacements that develop over time and where the sequence of the developments could be generated algorithmically by other functions: When multiple new and otherwise replacements are given, these are used by and by in their given order (circulating, if the end of the given sequence is reached). Replacements in the mapping can also be specified by functions returning the necessary replacement.
Note that sequence elements to replace can be arbitrary values, including subsequences.
map: old-new pairs (list of two-element lists)
sequence: sequence (list) to transform
otherwise: flat list of alternative replacements. Per sublist, only a single replacement value is chosen.
(developing-substitute '((1 (a b c)) (2 (h i j))) '(1 1 2 1 3 2 1) '(x y z)) => (a b h c x i a)
Beethoven-like condensation of music. All notes for which a given test function returns `nil' are removed, and their preceding note is extended by their duration.
NOTE: The first note of sequence cannot be removed (turned into a rest) by condensation.
sequence: An OMN sequence
test: Boolean function expecting individual parameters of each note in `OMN'
(setf my-motif '((q. c4 e d4 q. e4 e f4) (h g4 -h)))
(condense my-motif #'(lambda (dur pitch &rest other-args) (> (omn-encode dur) 1/4)))
Expects a list of pairs (<id> <no of repetitions>) and generates out of that a flat list of integers intended as ids for alternate-omns .
(section-id-seq '((0 2) (1 1) (0 3))) -> (0 0 1 0 0 0)
Generates one or more variations of given motif or phrase. As this function generates multiple variations, its result has one more list nesting level than `motif'.
The result can be used, e.g., as input for alternate-omns (alongside other motifs and their variations, which are possibly also generated with `vary-motif').
motif (OMN sequence or plain OMN parameter, depends on fn): a motif to vary
fn (function): arbitrary function transforming motif multiple times
argss (list of lists): lists of argument lists for `fn'. The argument `_' is a placeholder for `motif'.
(tu:one-level-flat (vary-motif '((h. b3g4 fp tie 3q g4 3e f4 stacc+leg g4 stacc+leg a4 stacc+leg b4 stacc+leg) (h. e4c5 marc)) #'pitch-transpose '((-4 _) (-2 _) (0 _))))
This function alternates between sublists of multiple omn sequences. It can be useful, e.g., to switch between different musical characteristics, but have some kind of development within each characteristic. This is a powerful function for organising musical form on a higher level.
The argument `seqs-of-seqs' can be written by hand or generated with other functions such as vary-motif and `ids' with functions like `gen-binary-rnd' or section-id-seq .
ids (list of 0-based integers): indicating the position of OMN expressions in seqs-of-seqs.
seqs-of-seqs (list of lists of OMN sequences or plain OMN parameters): specifies material between which to switch. The top-level list positions indicate the different materials pointed to by `ids'. The next level specifies the order in which material will be included when `ids' contains repetitions of an id. This order is circling. The lowest level is the actual material either as a flat list (each a single bar), or as a nested list (multiple bars). In case of a nested list at the lowest level you likely want to set `append' to T.
append? (Boolean): whether or not nested OMN sequences should be appended instead of been combined in a list.
The following example demonstrates the use of the arguments `ids' and `seqs-of-seqs' with a flat sequence of note lengths. Note the nesting of `seqs-of-seqs', as explained by the comments.
(alternate-omns ;; sequence of IDs '(0 0 1 1 0 0) '(;; ID 0 material/gestures (;; for first occurance of ID 0 (and again, when circling) (h q q) ;; second occurance of ID 0 etc. (h. e e)) ;; ID 1 material//gestures (only one segment, repeated always) ((e e e e e e e e)) )) ; => ((h q q) (h. e e) (e e e e e e e e) (e e e e e e e e) (h q q) (h. e e))
Now, the power of algorithmic composition lies in the fact that each of these arguments can be algorithmically generated. The next example demonstrates that. It also demonstrates how the materials/gestures can be OMN expressions with multiple parameters. Still, each of these gestures consist in a single bar (gestures are not nested, though they are nested within two list levels).
(alternate-omns ;; random sequence of IDs (gen-eval 10 '(rnd-pick '(0 1))) (list ;; sequence of first kind of musical gestures (make-omn :length (length-rest-series (rnd-sample 7 '(7 8 9)) (length-divide '(3 2) (rnd-sample 7 '((q q q) (h e e) (h.) (h q))))) :pitch '(d4 e4 f4 g4) :velocity '(pp)) ;; sequence of second kind of musical gestures (make-omn :length '(s s s s) :pitch (gen-rotate :right '(c5 d5 f5 a5 g5 e5) :type :seq) :velocity '(ff) :span :pitch)))
Below is a more concise algorithmic example demonstrating motivic variation. Here, IDs are generated randomly and motifs/phrases are specified manually. The function vary-motif is then used for transforming these motifs multiple times. Note that each motif consists of multiple bars, and results are appended using the argument `append?' (see below for more details on this argument).
(alternate-omns (gen-binary-rnd 1 10 3 1 :start 0) `(,(vary-motif '((q c5 p leg q e4 stacc) (q. f4 leg e g4 leg) (h a4)) #'rotate-omn '((0 _) (-1 _) (-2 _))) ,(vary-motif '((q e3c4 ff marc+stacc s c3 stacc e3 stacc f3 stacc g3 stacc)) #'pitch-transpose '((-6 _) (-4 _) (-2 _) (0 _) (2 _) (4 _)))) :append? T)
Remember that resulting OMN expressions can be 're-barred'. This is useful, e.g., for gestures that exceed a single bar but are still stored in a flat list (again, nested within two list levels).
(omn-to-time-signature (alternate-omns '(0 0 1 0 1 0 1 1 0 0 1 1 1) (list (make-omn :length (gen-rotate :left '(-1/20 1/20 1/20 1/20 1/20) :type :seq) :pitch '(d4 e4 f4 g4) :velocity '(ff)) (make-omn :length '(q e e) :pitch (gen-rotate :left '(c5 e5 f5) :type :seq) :velocity '(pp pp) :attribute '(ten stacc stacc) :span :pitch))) '(4 4))
Alternatively, it is possible to use gestures that consists of nested lists for representing bars (still nested within two higher list levels). In that case, you typically want to set `append?' to T so that the result is a standard OMN sequence with only a single level of nesting, as shown below.
(alternate-omns '(0 0 1 1 0 0) '((;; one nested gestures, circled in case of repeated IDs ((-h q c4 p leg) (q. f4 leg e g4 leg q a4 leg) (h. g4))) (;; one nested gestures ((q g4 f leg c5 leg e c4 stacc d4 stacc e4 f4 stacc) (q g4 stacc -h.)))) :append? T)
Variant of alternate-omns for scores.
ids (list of 0-based integers): indicating the position of scores in `seqs-of-scores'.
seqs-of-seqs (list of lists of scores): specifies material between which to switch. The top-level list positions indicate the different materials pointed to by `ids'. The next level specifies the order in which material will be included when `ids' contains repetitions of an id. This order is circling.
* Examples
(alternate-scores '(0 0 1) '(((:vln ((h c4 q d4 e4))) (:vln ((h e4 q d4 c4)))) ((:vln ((e b3d4g4 stacc stacc stacc stacc))))))
Alternate between fenvs and sample the fenvs; the result is a list of lists of numbers (fenv values). A fenv is a particularly flexible envelope, see my fenv library for details.
For convenience, fenvs can also be specified simply as lists of numbers (x-values).
ids (list of 0-based integers): indicating the position of fenvs.
ns (list of integers): indicates number of samples per fenv. `ids' and `ns' should have the same length.
fenv-lists (list of fenvs, or list of lists of fenvs): specifies fenvs between which to switch. If `fenv-lists' is a flat list of fenvs, then `ids' simply access the fenvs at those postions. If `fenv-lists' is nested, then `ids' indicate the top-level list positions of `fenv-lists'. The lower-level list of fenvs indicates alternatives from which to choose in order. So, when there is a repetition of integers in `ids', always the next fenv in the respective list of alternatives is choses. This order is circling. Remember that fenvs can be specified as a number sequence as well.
interpolation (either :steps or :linear): in case fenvs are specified as lists of numbers, the `interpolation' indicates the type of fenv created.
Using lists of integers, internally translated into fenvs. The result here is rather similar to the input, but with lists of different lengths.
(alternate-fenvs '(0 1 0) '(3 3 4) '((1 2 3 4) (10 8 6 4))) => ((1 3 4) (10 6 4) (1 2 3 4))
For ns greater than the length of the number lists representing the fenvs, the interpolation method makes a difference.
(alternate-fenvs '(0 1 0) '(6 8 6) '((1 3 2) (10 4))) => ((1 1 3 3 2 2) (10 10 10 10 4 4 4 4) (1 1 3 3 2 2))
(alternate-fenvs '(0 1 0) '(6 8 6) '((1 3 2) (10 4)) :interpolation :linear) => ((1 9/5 13/5 14/5 12/5 2) (10 64/7 58/7 52/7 46/7 40/7 34/7 4) (1 9/5 13/5 14/5 12/5 2))
When specifying fenvs directly, the full range of fenvs are available.
(alternate-fenvs '(0 1 0) '(3 3 4) (list (fenv:sin-fenv (0 1) (1 0)) (fenv:sin-fenv (0 0) (1 1))))
An example with double-nested fenvs
(alternate-fenvs '(0 0 1 1 0 0) '(4 4 4 4 4 4) '(((1 2 3 4) (2 3 4 5) (3 4 5 6)) ((10 8 6 4) (11 9 7 5)))) => ((1 2 3 4) (2 3 4 5) (10 8 6 4) (11 9 7 5) (3 4 5 6) (1 2 3 4))
For more examples with nested lists of fenvs in `fenv-lists' compare the use of (double) nested OMN sequences in `alternate-omns' above.
aux fn is a function expecting a list of sublists of sequence
alternate-subseq-fenvs | ids no-of-sublists fenv-lists parameter sequence &key (min-vel 'pp) (max-vel 'ff) (articulation-maps nil) (keep-articulations? t) (interpolation :steps) (hairpins? nil) | [Function] |
This function adds (or replaces) a given `parameter' to (of) an OMN `sequence', where this new parameter sequence follows a concatenation of fenvs. A fenv is a particularly flexible envelope, see my fenv library for details.
Like `alternate-omns', the present function is useful for switching between different musical characteristics, while having some kind of development within each characteristic. It is a powerful function for organising musical form on a higher level.
One of the particular expressive powers of this function is that characteristics defined by fenvs (`fenv-lists') can be applied to sublists (bars) in `sequence' of different lengths. For example, the same overall gestus can be applied to a bar of three but all four or five notes. (Nevertheless, it does not allow to change the number of elements in `sequence'.)
For convenience, fenvs can also be specified simply as lists of numbers (x-values).
See also the function `alternate-omn-fenvs', which is similar but more easy to use.
ids (list of 0-based integers): indicating order of positions of fenvs to choose from `fenv-lists'.
no-of-sublists (integer or list of integers): indicates how many consecutive sublists (quasi bars) of `sequence' each fenv shapes. The number of samples created from each fenv is the product of the length of the sublist in question and the respective `no-of-sublists' value.
fenv-lists (list of fenvs, or list of lists of fenvs): specifies fenvs between which to switch. If `fenv-lists' is a flat list of fenvs, then `ids' simply access the fenvs at those positions. If `fenv-lists' is nested, then `ids' indicate the top-level list positions of `fenv-lists'. The lower-level list of fenvs indicates alternatives from which to choose in order. So, when there is a repetition of integers in `ids', always the next fenv in the respective list of alternatives is choses. This order is circling.
parameter (keyword): the parameter the fenvs overwrite in `sequence', can be :length, :pitch, :velocity or :articulation. As fenv values are always numeric, they have to be translated into the corresponding parameter. Fenvs values for length values should be ratios (they are translated to ratios internally, but complex ratios can lead to difficulties with notation); fenv values for pitches are MIDI note numbers; fenv values for velocities depend on `min-vel' and `max-vel'; and articulations are the rounded position of elements in the `articulation-maps'.
sequence: OMN sequence to transform, must be nested.
min-vel/max-vel (OMN velocity symbol like 'p or 'ff): in case `parameter' is :velocity, these args set the minimum and maximum velocity in the result.
articulation-maps (list of list of OMN articulation symbols): in case `parameter' is :articulation, this arg specifies the articulations that rounded fenv values mean. For example, if `articulation-maps' is '(stacc ten) then the fenv value 0 results in a staccato and 1 in a tenuto.
keep-articulations? (Boolean): if T, existing articulations of `sequence' are retained and new articulations are added, otherwise new articulations overwrite the existing ones.
interpolation (either `:steps' or `:linear'): in case fenvs are specified as lists of numbers, the `interpolation' indicates the type of fenv created.
hairpins? (Boolean): In case `parameter' is `:velocity' you can set that the dynamics are connected with crescendo and diminuendo hairpins, which may make particularly sense if `interpolation' is set to `:linear'.
Lists of integers can be used instead of fenvs for convenience.
(alternate-subseq-fenvs '(0 1 1 0) '(2 1 2 1) ;; two envelopes, defined as linearly interpolated number lists '((72 48) (60 84)) :pitch (gen-repeat 3 '((h h) (q q q q))) :interpolation :linear)
When specifying fenvs directly, the full range of fenvs and their transformations are available.
(alternate-subseq-fenvs '(0 1 1 0) '(2 1 2 1) (list (fenv:sin-fenv (0 72) (1 48)) (fenv:sin-fenv (0 60) (1 84))) :pitch (gen-repeat 3 '((h h) (q q q q))))
An example with velocities
(alternate-subseq-fenvs '(0 1 1 0) '(1 1 1 1) (list (omn->fenv :velocity '(p p p ff) :type :steps) (omn->fenv :velocity '(mf pp pp pp) :type :steps)) :velocity (gen-repeat 3 '((h h) (q q q q))))
An example with double-nested lists.
(alternate-subseq-fenvs '(0 0 1 1 0 0) '(1 1 1 1 1 1) ;; two envelopes, defined as linearly interpolated number lists '(((72 60) (71 59) (70 58)) ((72 84) (74 86))) :pitch (gen-repeat 3 '((h h) (q q q q))) :interpolation :linear) => ((h c5 c3) (q b4 eb4 g3 b2) (h c4 c6) (q d4 bb4 fs5 d6) (h bb4 bb2) (q c5 e4 gs3 c3))
[Aux] Expects a list returned by an expression like (omn :leg sequence), which expresses TRUE with 1 and FALSE with -1, but consecutive TRUE or FALSE values are accuulated, so that '(1 1 1 -1 1) is represented more concisely as '(3 -1 1). The present function translates this more concise representation into the longer representation consisting only of 1 and -1, so that results of an expression like (omn :leg sequence) can be turned into a fenv.
Translates one of the parameters of the OMN `sequence' into a number sequence, which is then translated into a fenv. A fenv is a particularly flexible envelope, see my fenv library for details.
This function is useful, e.g., for quasi motific variation, where the number of notes in the result changes but the result still follows the overall shape (profile) of the given music.
Usually, you do not want to use this function directly, but instead use the function alternate-omn-fenvs , which is less low-level and therefore more easy to use.
parameter: an OMN parameter keyword like :length or :pitch
sequence: either full OMN sequence or sequence of the respective OMN parameters
type (either :steps or :linear): how to interpolate between parameter values. Can be :steps (a step function, i.e. parameter values are hold by the fenv until the next value) or :linear (linear interpolation).
x-values (either :rhythm or :equidistant): whether the x-values of the resulting fenv follow the rhythm of `sequence' or are equidistant, where points are spread evenly across the x axis.
A six-note OMN sequence with multiple parameters used for several examples
(setf sequence '((h c5 f ten q g4 mp stacc) (q b4 ff ten q g4 f stacc q d4 mp stacc) (h. g4 f tr2)))
Translate the note values of this six-note sequence into a sequence of five note values.
(fenv:fenv->list (omn->fenv :length sequence) 5)
Translate the pitches of this six-note sequence into a sequence of five pitches.
(midi-to-pitch (fenv:fenv->list (omn->fenv :pitch sequence) 5))
Translate the pitches of this six-note sequence into a sequence of eight pitches.
(midi-to-pitch (mapcar #'round (fenv:fenv->list (omn->fenv :pitch sequence :type :linear) 8)))
Translate the note dynamics of this six-note sequence into a sequence of 9 dynamics without interpolation.
(get-velocity (mapcar #'round (fenv:fenv->list (omn->fenv :velocity sequence :type :steps) 9)) :type :symbol)
Translate the note dynamics of this six-note sequence into a sequence of 5 dynamics, but linearily interpolating between the original xones.
(get-velocity (mapcar #'round (fenv:fenv->list (omn->fenv :velocity sequence :type :linear) 5)) :type :symbol)
Translate the articulations of this six-note sequence into a sequence of 7 articulations.
(multiple-value-bind (fenv arts-set) (omn->fenv :articulation (flatten sequence)) ;; inefficient with list (mapcar #'(lambda (i) (nth i arts-set)) (fenv:fenv->list fenv 7)))
omn->fenvs | sequence &key (parameters '(:length :pitch :velocity)) (type :steps) (x-values :rhythm) | [Function] |
Translates all parameters of the OMN `sequence' into a number sequences, which are then translated into a fenvs. Returns a plist of pairs <parameter> <fenv>. For further details on how the individual fenvs are generated see function `omn->fenv'
sequence: OMN sequence
parameters: which parameters to translate. By default, leaves out :articulation, because interpreting articulations in a fenv takes some extra measures.
type: see doc of omn->fenv above
x-values: see doc of omn->fenv above
Translates a fenv into one of the OMN parameters. This is the inverse of the above function {defun omn->fenv}. For translating fenvs into OMN attributes, a list of attributes must be given.
fenv (a fenv)
parameter: an OMN parameter keyword like :length or :pitch
n (integer): how many values to generate from fenv.
arts-set (list of articulation symbols): this list maps number (fenv values) into the attributes at the corresponding position in `arts-set'.
alternate-omn-fenvs | ids no-of-sublists omn-fenv-lists parameters sequence &key (keep-articulations? t) (interpolation :steps) (hairpins? nil) | [Function] |
This function adds (or replaces) one or more given `parameters' to (of) an OMN `sequence', where this new parameter sequence follows a concatenation of fenvs. A fenv is a particularly flexible envelope, see my fenv library for details. However, for this function musical characteristics in `omn-fenv-lists' are defined by OMN expressions instead, and fenvs are only used in the background.
Like `alternate-omns', the present function is useful for switching between different musical characteristics, while having some kind of development within each characteristic. It is a powerful function for organising musical form on a higher level.
One of the particular expressive powers of this function is that characteristics defined by fenvs (`fenv-lists') can be applied to sublists (bars) in `sequence' of different lengths. For example, the same overall gestus can be applied to a bar of three but all four or five notes. (Nevertheless, it does not allow to change the number of elements in `sequence'.)
ids (list of 0-based integers): indicating order of positions of fenvs to choose from `omn-fenv-lists'.
no-of-sublists (integer or list of integers): indicates how many consecutive sublists (quasi bars) of `sequence' each fenv shapes. The number of samples created from each fenv is the product of the length of the sublist in question and the respective `no-of-sublists' value.
omn-fenv-lists (list of flat OMN expression lists, or list of lists of flat OMN expression lists): specifies characteristics between which to switch (transformed into fenvs in the background). If `omn-fenv-lists' is a list of OMN expression lists, then `ids' simply access the fenvs at those positions. If `omn-fenv-lists' is further nested, then `ids' indicate the top-level list positions of `omn-fenv-lists'. The lower-level list of fenvs indicates alternatives from which to choose in order. So, when there is a repetition of integers in `ids', always the next fenv in the respective list of alternatives is choses. This order is circling.
parameter (keyword or list of keywords): the parameter the fenvs overwrite in `sequence', can be :length, :pitch, :velocity or :articulation.
sequence: OMN sequence to transform, must be nested.
keep-articulations? (Boolean): if T, existing articulations of `sequence' are retained and new articulations are added, otherwise new articulations overwrite the existing ones.
interpolation (either `:steps' or `:linear'): `interpolation' indicates the type of fenv created (interpolation between the numeric representation of OMN parameter values or not).
hairpins? (Boolean): In case `parameter' is `:velocity' you can set that the dynamics are connected with crescendo and diminuendo hairpins, which may make particularly sense if `interpolation' is set to `:linear'.
TODO:
Rotate the OMN `sequence' by `n' positions.
n: an integer (rotation number, positive or negative) or a list of ints. The keywords :left and :right are equivalents of the integer values -1 and 1. If `n' is a list then `flat' must be nil.
sequence: OMN expression.
parameter (keyword): which parameter to rotate (e.g., :length, :pitch...).
flat (Boolean): whether to rotate the full flattened sequence (T) or subsequences.
section (list of ints): positions of sublists to process. This argument is ignored if flat is T.
(rotate-omn :right '((-h q c4) (q. f4 e g4 q a4) (h. g4))) (rotate-omn :left '((-h q c4) (q. f4 e g4 q a4) (h. g4)) :parameter :length) (rotate-omn 2 '((-h q c4) (q. f4 e g4 q a4) (h. g4)) :section '(1 2) :flat nil)
The following examples are rotating subsequences separately.
(rotate-omn '(0 1 2) '((-h e c4 e4) (q. f4 e g4 q a4) (q g4 f4 e e4 d4)) :flat nil) ; default parameter pitch (rotate-omn '(0 1 2) '((-h e c4 e4) (q. f4 e g4 q a4) (q g4 f4 e e4 d4)) :flat nil :parameter :length) (rotate-omn '(2 1) '((-h e c4 e4) (q. f4 e g4 q a4) (q g4 f4 e e4 d4)) :section '(1 2) :flat nil :parameter :length)