Functor
Measure ("/Users/torsten/oz/music/Strasheela/strasheela/trunk/strasheela/contributions/anders/Measure/Measure.oz")
Import
- FD
- FS
- GUtils at "x-ozlib://anders/strasheela/source/GeneralUtils.ozf"
- LUtils at "x-ozlib://anders/strasheela/source/ListUtils.ozf"
- Score at "x-ozlib://anders/strasheela/source/ScoreCore.ozf"
- Pattern at "x-ozlib://anders/strasheela/Pattern/Pattern.ozf"
- HS at "x-ozlib://anders/strasheela/HarmonisedScore/HarmonisedScore.ozf"
- Out at "source/Output.ozf"
Export
Define
Measure is a silent timed element representing the meter of a score for the duration of the Measure. Measures are contained in a silent MeasureSeq/UniformMeasures and a MeasureSeq/UniformMeasures runs in parallel to the actual (i.e. sounding) score. That way, the metric structure is freely constrainable independent of other parameters and the hierarchic structure of the rest of the score.
The Measure parameters 'beatNumber' and 'beatDuration' represent the meter of the measure. For instance, in case the time unit of the score is beats(4) and thus 4 represents a quarter note (see doc Score.timeMixin) then the meter 3/8 is represented by beatNumber=3 and beatDuration=2. Usually, beatDuration will equal the number of ticks per beat defined in the time unit of the score (exceptions are needed in case the score contains measures with different beat durations).
The attribute 'beats' (a list of FD ints) represents the relative startTimes of the beats in the measure (i.e., the start times counting from the beginning of the measure). For instance, in case beatNumber=3 and beatDuration=2 then beats=[0 2 4].
The attribute 'accents' (a list of FD ints) represents the relative startTimes of the strong beats in the measure. The strong beats depend on the beatNumber of the measure. The accent patterns in common praxis music are highly standardised (e.g. the meter 4/4 has strong beats on the first and the third beat). Nonetheless, Measure allows to freely define these accent patterns for each possible beatNumber value for each Measure at the optional init method argument 'accentIdxDB'. This argument expects a record (with only integer features) of lists of integers to specify an accent pattern for each beatNumber. For instance, the specification of the common praxis tripel and quadruple meter is unit(3:[1] 4:[1 3]).
The default of 'accentIdxDB' defines the usual common praxis accent patterns for single (1/2, 1/4, 1/8), duple (2/2, 2/4, 2/8), triple (3/2, 3/4, 3/8), and quadruple (4/2, 4/4, 4/8) meter, as well as compound duple (6/2, 6/4, 6/8), compound triple (9/4, 9/8), and compound quadruple (12/4, 12/8, 12/16) meter. The compound meters may also be used to express 'prolatione perfecta' for old music, while the non-coumpound meters express 'prolatione imperfecta'. For the quintuple meter (5/4 etc.), the accent pattern 3/4 + 2/4 is the default.
NB: the initialisation constraints for both beats and accents are delayed until beatNumber is determined (i.e. in a CSP the beatNumber of each beat is either predetermined or the distribution strategy should determine all beatNumbers before other parameter values which are related to the measure by constraints).
Note that the class UniformMeasures is more comprehensive than this class.
class Measure from Score.temporalElement
feat !MeasureType label
- init(accentIdxDB:AIs beatDuration:BeatDuration beatNumber:BeatNr ...)
- getBeatNumber(X)
- getBeatNumberParameter(X)
- getBeatDuration(X)
- getBeatDurationParameter(X)
- getBeats(X)
- getBeatsFS(X)
- getAccents(X)
- getAccentsFS(X)
- getInitInfo($ ...)
end
fun{IsMeasure X}
UniformMeasures is a silent timed element representing a constant meter of a score for the duration of the UniformMeasures. The parameter 'n' represents the number of measures. The parameter 'beatNumber' and 'beatDuration' represent the meter as in Measure. The further measure parameters and features (e.g. 'beats' and 'accents') are accessible by the same methods as in Measure. However, there is a difference between getDuration and getMeasureDuration: the first returns the duration of the whole UniformMeasures while the second the duration of a single measure.
The similar class MeasureSeq allows to represent a sequence of measures which differ in beatNumber or beatDuration. However, this makes the constraints for MeasureSeq less efficient then the constraints for UniformMeasures.
NB: The constraints defined for UniformMeasures required both BeatNr and BeatDuration to be determined.
See the Measures doc for more info.
class UniformMeasures from Score.temporalElement
feat !UniformMeasuresType label
- init(beatDuration:BeatDuration beatNumber:BeatNr n:N ...)
- getN(X)
- getNParameter(X)
- getBeatNumber(X)
- getBeatNumberParameter(X)
- getBeatDuration(X)
- getBeatDurationParameter(X)
- getBeats(X)
- getBeatsFS(X)
- getAccents(X)
- getAccentsFS(X)
- getMeasureDuration(X)
- getMeasureDurationParameter(X)
- getMeasureAt(I Time)
- getAccentInMeasureAt(I Time)
- getBeatAt(I Time)
- getBeatInMeasureAt(I Time)
- onMeasureStartR(B Time)
- onMeasureStartDR(B Time)
- onAccentR(B Time)
- onBeatR(B Time)
- onBeatDR(B Time)
- overlapsBarlineR(B Start End)
- measureSyncopationR(B Start End)
- accentSyncopationR(B Start End)
- beatSyncopationR(B Start End)
- getInitInfo($ ...)
end
fun{IsUniformMeasures X}
AccentRatingMixin extends note classes with an accent rating parameter. No further constraints are applied.
class AccentRatingMixin
feat !AccentRatingType
- initAccentRatingMixin(accentRating:AR)
- getAccentRating(X)
- getAccentRatingParameter(X)
end
fun{IsAccentRatingMixin X}
fun{MakeAccentRatingClass SuperClass}
[concrete class constructor] Expects a note class, and a class that is extended with an accent rating parameter (see AccentRatingMixin).
proc{Accent_If N AccentConstraints Args}
Imposes constraint on self which associates certain BeatNr values to their typical Accent pattern in common praxis. This constraint is applied to self if the init arg isClassic equals true, which is the default.
For non-classical measure types (e.g. 9/8 as 2/8 + 2/8+ 2/8 + 3/8) don't use this constraint (i.e. init arg isClassic=false) and apply alternative constraints.
Accents are 0 based, i.e. an accent at 0 is an accent at the measure start.
*/
meth classicAccents
!!?? could selection constraints help here to have better propagation (e.g. defining a database of beatNumber and accent patterns and deciding for an index)
BeatNr = {self getBeatNumber($)}
Accents = {self GetAccents($)}
in
{Pattern.disjAll
[{FD.conj
(BeatNr=:4) % quadruple (4/2, 4/4, 4/8)
{FS.reified.equal Accents {FS.value.make [0 2]}}}
{FD.conj
(BeatNr=:3) % triple (3/2, 3/4, 3/8)
{FS.reified.equal Accents {FS.value.make [0]}}}
{FD.conj
(BeatNr=:2) % duple (2/2, 2/4, 2/8)
{FS.reified.equal Accents {FS.value.make [0]}}}
{FD.conj
(BeatNr=:1) % single (1/2, 1/4, 1/8)
{FS.reified.equal Accents {FS.value.make [0]}}}
{FD.conj
(BeatNr=:6) % compound duple (6/2, 6/4, 6/8)
{FS.reified.equal Accents {FS.value.make [0 3]}}}
{FD.conj
(BeatNr=:9) % compound triple (9/4, 9/8)
{FS.reified.equal Accents {FS.value.make [0 3 6]}}}
{FD.conj
(BeatNr=:12) % compound quadruple (12/4, 12/8, 12/16)
{FS.reified.equal Accents {FS.value.make [0 3 6 9]}}}
{FD.conj
(BeatNr=:5) % quintuple 2/4 + 3/4
{FS.reified.equal Accents {FS.value.make [0 2]}}}
{FD.conj
(BeatNr=:5) % quintuple 3/4 + 2/4
{FS.reified.equal Accents {FS.value.make [0 3]}}}
further meters may be defined here (adjust else case):
else case
{FD.conj
{FD.disj
{FS.reified.include BendBeatNr {FS.value.make [7 8 10 11]}}
(BendBeatNr>:12)}
{FS.reified.equal Accents {FS.value.make [0]}}}]
1}
end
Nachdenken:
attr beatNumber und beatDuration definieren measure vollstaendig (2 entscheidungen noetig)
implizit:
* beats (list, tuple of FD or FS): relative startZeiten der beats in measure
Formalism: arithmetic series: number beatNumber, starts with 0, difference beatDuration
* accents (list, tuple of FD or FS): either relative startZeiten der akzentuierten/betonten beats oder indices der akzentuierten beats.
Formalism: indices are selected dependent on beatNumber. the relative startTimes are then the beats at these indices
Problemchen: wenn ich beats und accents entsprechend modelliere und per default constraine, dann gibt es keine billigere Variante mit weniger Propagatoren mehr.. -- verwende extra alternative Klasse UniformMeasureSeq instead of MeasureSeq.
Benoetigte Constraints:
* propagiere beatNumber zu beats length/card (and indirectly to accents)
-> erledigt, wenn beatNumber erst determiniert sein muss
- FS: FS.card
- tuple: RecordC.width (what effect has this propagator?)
- list/stream ??
* numerische constraints (arithmetic series),
- FS ?? (FS.forAllIn needs determined set)
- OK: list/stream: Pattern constraint
- OK: tuple ?? (Record.forAll blocks for undetermined RecordC. However, when I somehow add features to RecordC I know these (and may also monitor them))
* propagiere beatNumber zu decision for accent indices into beats
Simplification: no constraint and no special case for different 5 meters: optional arg is tuple with accentPatterns (features represent beatNumber) and once beatNumber is determined the appropriate accentPattern is taken out of the tuple -- keep old constraint solution in a comment as an alternative more general proposal
-> erledigt, wenn beatNumber erst determiniert sein muss
- FS:
+ large reified disj using FS.reified.equal and others
+ Select from database with entries like unit(beatNumber accents)
- Tuple/List ?? (option as soon as beatNumber is determined: or combinator, also selection constraints)
* access accents in beats dependent on indices into beats,
- FS: ?? (how to access the ith element in a FS as a FD int -- there is no order in a set!: selection constraints?)
- OK? tuple/list: selection constraint (?? needs length of both beats and accents determined?)
* actual score item constraint: reified include/select some time in beats/accents (in onAccentR/onBeatR)
- OK: local (memorised ?) FS (matched to data): FS: FS.reified.include
- tuple/list: ?? (crude reified Select?)
-> Ergo: FS keine option: mir fehlt Reihenfolge (access at index in Set) und ich kann numerische constraints nicht recht anwenden
-> list / tuple sind moegl. aber blockieren bis beatsNr determiniert ist. Auch hier Problem: wie
-> sobald beatsNr determiniert ist kann ich representationen FS und list/tuple matchen (FS.int.match, braucht determinierten vector von FD ints) und so constraints aus beiden Welten haben (zusaetzl. vars, speicherhungrig..)
FS toot Chap. 4 kombiniert FS and RecordC, aber da ist width und aritity of RecordC bekannt und RecordC erlaubt nur ein etwas kuerzeres script.
-> !!!! Entscheidung: beatsNr muss moeglichst frueh determiniert werden: application of constraints wird bis dahin delayed (dies keinerlei Beschraenkung fuer alte Musik bis 1900, da hier Takt ueblicherweise ohnehin am Anfang des Kompositionsprozesses festgelegt wird)
Vague idea: I may use BeatNr as index for two selection constraints to select (a) the accents (FS) and (b) the beats (FS). Warscheinlich SchnapsIdee: beats abhaengig von sowohl beatNumber als auch beatDuration (kann nicht einfach selectiert werden) und die numerischen Constraints kann ich mit FS immer noch nicht ausdruecken
Accent constraints
Constraint applicators: Accent_If etc.
/** %% With Accent_If various musical aspects and parameters can be constrained so that the resulting music expresses the underlying metric structure (simultaneous measure objects). This constraint applicator is inspired by the chapter on rhythm in Berry, Wallace. 1987. Structural Functions in Music. Courier Dover Publications.
The start time of N coincides with the given "position" in a simultaneous measure (e.g., the measure's start or any accentuated beat), if given a list of given conditions is fulfilled well enough. These conditions (AccentConstraints) are a list of unary functions: the input is N and the return value is a rating of N (an FD int), where 0 means condition not fulfilled and higher values mean that the condition is increasingly better fulfilled. The sum of the return values of all conditions must be equal or exceed a given threshold (arg minRating) in order to trigger that the start time of N is constrained to a certain metric position. Predefined accent constraints include IsLongerThanSurrounding and IsHigherThanSurrounding (see their documentation for further details).
Args:
metricPosition (FD int or atom, default 'accent'): if N sufficiently meets the conditions, then its start time is constrained to this "position" in the measure. The following values are supported.
measureStart: N starts with a measure
accent: N starts with a strong beat (depends on the measure definition)
beat: N starts with a beat
an FD int: N starts at a specified time within a measure (e.g., if 0 then N starts on measure start, if 1 it starts on measure start + 1 etc.). Should not be larger than the measure duration.
minRating (FD int, default 1): Minimum accumulated rating of accent constraint outputs. If the sum of the return values of all accent constraints are equal or exceed a minRating, then in order the start time of N is constrained to the metric position metricPosition.
strictness (atom, default 'note'): Must the constrained be fulfilled for all notes meeting the criteria or for all given metric positions? There are three different cases.
note: if note/item meets the accent criteria it must be on a specified metric position, but even if this constraint is applied to all notes there can be accentuated metric positions without notes meeting such criteria
position: if note/item is at a specified metric position then it must meet the accent criteria, but even if this constraint is applied to all notes there can be accentuated notes at other positions.
noteAndPosition: if note/item meets the accent criteria it is on a specified metric position and vice versa
NOTE: none of the possible values for strictness enforces that there actually starts a note at any metric position specified in metricPosition. Use the constraint NoteAtMetricPosition for this purpose.
toplevel (default false): The container in which N is contained that should be considered the top level for finding the simultaneous measure object (if false, then the whole score is searched). This argument is for optimisation purposes only.
measureTest (default IsUniformMeasures): A Boolean function that returns true for the relevant measure objects. (currently only works with uniform measures?)
rating (an FD int): this argument is bound to the accumulated rating of accent constraint outputs for N. This variable can that way be constrained outside the call of Accent_If (e.g., to constrain the accent structure of some musical section, the number of occurances of some minumum rating or the minimum sum of ratings over multiple notes can be constrained).
Note: if N inherited from IsAccentRatingMixin then the rating is automatically added to its parameter accentRating. Therefore, Accent_If (or SetAccentRating) should only be called once for such a note.
It is often good practice to combine all accent constraints into a single rating anyway. Exceptions would be special cases where, e.g., accents expressed by duration-relations and accents expressed by pitch-relations should fall on different metric positions. In such cases it is sufficient to avoid using notes that inherited from IsAccentRatingMixin.
proc{Accent_If2 N Args}
Same as Accent_If, but no accent constraints are applied (use SetAccentRating for this). This can be useful, e.g., to combine multiple calls of Accent_If2, say, with different values for Args.metricPosition and Args.minRating.
proc{SetAccentRating N AccentConstraints Args}
SetAccentRating sets an accumulated accent rating for the note N. AccentConstraints are a list of unary functions: the input is N and the return value is an accent rating of N (an FD int), where 0 means no accent and higher values mean a stronger accent. SetAccentRating sets the accent rating of N to the sum of all AccentConstraint results. Predefined accent constraints include IsLongerThanSurrounding and IsHigherThanSurrounding (see their documentation for further details).
Args:
'rating' (an FD int default {FD.decl}): this argument is bound to the accumulated rating of accent constraint outputs for N. This variable can that way be constrained outside the call of SetAccentRating (e.g., shared with Accent_If, or constrain the accent structure of some musical section, the number of occurances of some minumum rating or the minimum sum of ratings over multiple notes can be constrained).
If N inherited from IsAccentRatingMixin then the rating is automatically added to its parameter accentRating. Therefore, SetAccentRating (or Accent_If) should only be called once for such a note.
It is often good practice to combine all accent constraints into a single rating anyway. Exceptions would be special cases where, e.g., accents expressed by duration-relations and accents expressed by pitch-relations should fall on different metric positions. In such cases it is sufficient to avoid using notes that inherited from IsAccentRatingMixin.
proc{NoteAtMetricPosition MyMeasure Notes Args}
TODO: doc
Measure (a UniformMeasures instance)
Args:
metricPosition (FD int or atom, default 'accent'):
measureStart: one or more element of Notes starts with Measure
accent: one or more element of Notes starts with any accent of Measure
beat: one or more element of Notes starts with any beat
an FD int: one or more element of Notes starts at a specified time within a measure (e.g., if 0 then N starts on measure start, if 1 it starts on measure start + 1 etc.). Should not be larger than the measure duration.
allowRestsAtMetricPosition (Boolean, default false): if true, then instead a note start there can be a rest at the metric positions in question introduced by a note's offset time > 0.
?? Note: constraint application delayed until Measure is fully determined.
BUG: unfinished definition. Only defined for case metricPosition:measureStart so far
fun{Make_HasAtLeastDuration Dur}
Returns an accent constraint (a function execting a note/item and returning a rating FD int). This resulting function returns 1 for notes with a duration of Dur or longer, and 0 otherwise.
fun{IsFirstItem N}
B=1 <=> Note N is the first item in its container.
fun{IsLongerThanDirectNeighbours N}
B=1 <=> Note N is longer than both its preceeding and its succeeding note (duration + offsetTime used for calculating the perceived duration). If a preceeding or succeeding note does not exist (in the same temporal container) then that part of the condition is considered to be fulfilled.
fun{IsLongerThanPredecessor N}
B=1 <=> Note N is longer than the preceeding note and not shorter than succeeding note (duration + offsetTime used for calculating the perceived duration). If a preceeding or succeeding note does not exist (in the same temporal container) then that part of the condition is considered to be fulfilled.
fun{IsLongerThanPredecessorSimple N}
B=1 <=> Note N is longer than the preceeding note (duration + offsetTime used for calculating the perceived duration). If a preceeding or succeeding note does not exist (in the same temporal container) then that part of the condition is considered to be fulfilled.
proc{IsLongerThanPredecessor_Rated N Rating}
The higher the value of Rating, the more N is accented by its duration compared to its preceeding note (duration + offsetTime used for calculating the perceived duration).
Rating=1: N is longer than its predecessor, or if there exists no predecessor.
Rating=2: N is at least 2 times as long as its predecessor.
Rating=3: N is at least 4 times as long as its predecessor.
Rating is 0 otherwise. Rating is also 0 if N is shorter than its succeeding note.
proc{IsLongerThanSurrounding_Rated N Rating}
The higher the value of Rating, the more N is accented by its duration compared to its surrounding notes.
Note: The rating of the first note in a temporal container is limited to the range [1,2].
fun{IsFirstOfEqualNoteValues N}
B=1 <=> Note N is the first of 2 or more notes with equal note values (duration + offsetTime used for calculating the perceived note value), but the preceeding note value is different.
If a preceeding note does not exist (in the same temporal container) then that part of the condition is considered to be fulfilled, but a succeeding note must exist for B=1.
fun{IsHigherThanDirectNeighbours N}
B=1 <=> Note N's pitch is higher than both its preceeding and its succeeding note. If a preceeding or succeeding note does not exist (in the same temporal container) then that part of the condition is considered to be fulfilled.
TODO: ?? take offset times into account: a note with an offset time > 0 has "no predecessor". If the successor has an offset time > 0 then it has "no successor".
fun{IsHigherThanPredecessor N}
B=1 <=> Note N's pitch is higher than the preceeding note and not lower than succeeding note. If a preceeding or succeeding note does not exist (in the same temporal container) then that part of the condition is considered to be fulfilled.
TODO: ?? take offset times into account: a note with an offset time > 0 has "no predecessor". If the successor has an offset time > 0 then it has "no successor".
fun{IsSkip N}
B=1 <=> Note N's pitch skips from its preceeding note by more than a minor third in either direction. If a preceeding note does not exist (in the same temporal container) then the condition is considered not to be fulfilled.
TODO:
- ?? take offset times into account: a note with an offset time > 0 has "no predecessor". If the successor has an offset time > 0 then it has "no successor".
- !! Variant for large skips (see [Berry, 1987, p. 339, point 2]
proc{IsHigherThanPredecessor_Rated N Rating}
The higher the value of Rating, the more N is accented by its pitch compared to its preceeding note.
Rating=1: N is higher than its predecessor, or if there exists no predecessor.
Rating=2: N is more than major second higher than predecessor
Rating=3: N is more than fourth higher than predecessor
Rating=4: N is more than major six higher than predecessor
Rating is 0 otherwise. Rating is also 0 if N is lower than its succeeding note.
proc{IsHigherThanSurrounding_Rated N Rating}
The higher the value of Rating, the more N is accented by its pitch compared to its surrounding notes.
Note: The rating of the first note in a temporal container is limited to the range [1,2].
proc{HasTextureAccent MyNote Rating}
Rating (FD int) is the number of simultaneous notes that have the same start time as MyNote (a note object). There may be more simultaneous notes with a different start time, but these do /not/ count.
Note: Constraint delayed until simultaneous notes are known.
fun{WeightConstraint C I}
Expects an accent constraint C (a function expecting a note and returning a rating FD int, and returns an accent constraint that is a variation of C, where the rating is multiplied by I (an FD int).
fun{Make_HasAnacrusis Args}
Make_HasAnacrusis returns an accent constraint, i.e. a function execting a note/item N and returning a rating FD int. The resulting function returns a positive rating for N preceeded by an anacrusis, and 0 otherwise.
Args:
context (record or function, default predecessorsUpToRest): This argument specifies the score context that potentially forms an anacrusis of N. If 'predecessorsUpToRest', then the notes before N up to any rest (offset time or pause object) are taken into account (within the same temporal container). If predecessors(I), then the I (an int) notes before N are taken into account (within the same temporal container). The context can also be defined by a unary function expecting N and returning the items as a list.
ratingPs (list of constraints {P Xs ?Rating}, default nil): This argument specifies how the quality (rating) of an anacrusis is measured. Each ratingP is a function that expects a list of notes (of at least length 2) starting with N, then its predecessor and so forth. Each function returns a rating (an FD int). The resulting accent constraint rating is the minimum rating of any ratingP (subject to requirements, see below). Example constraint: N predecessors are of equal length (Anacrusis_FirstNEvenDurations).
requirements (list of reified constraints {P Xs B}, default nil): This argument specifies requirements that must be met by the score context if it should count at all as an anacrusis. Each requirement is a function that expects a list of notes (of at least length 2) starting with N, then its predecessor and so forth. Each function returns a 0/1-int. If any requirement returns 0 then the accent constraint returns 0 for this note. If all requirements returns 1, then the value resulting from the ratingPs is returned as rating. Example constraint: N longer than its predecessor (Anacrusis_LongerThanPrevious).
maxRating (int, default 2): maximum rating for N. If the computed rating exceeds maxRating then maxRating is returned instead.
Note: if neither ratingPs nor requirements are given then the accent constraint returns the rating 1 for all notes.
fun{Anacrusis_AccentLonger Ns}
[anacrusis requirement] The duration of the accent (1st note in Ns) is longer then the first note of the anacrusis (the 2nd note in Ns).
fun{Anacrusis_DirectionChange Ns}
[anacrusis requirement] At the accent (1st note in Ns) happens a change in pitch direction.
fun{Anacrusis_LocalMax Ns}
[anacrusis requirement] The accent (1st note in Ns) is a local pitch maximum.
End