Index
HarmonisedScore
DB
HS_Score
Rules
DBs
HS_Distro
HS_Out
HS
Schoenberg
Default
Jazz
Partch
Johnston
Harrison
Chalmers
Catler
ArithmeticalSeriesChords
This functor defines several general rules/constraints (i) on chords and/or scales and (ii) melodic rules etc.



Background info to melodic rules: conventional non-harmonic note pitch conditions (according to Piston. Counterpoint, Norton, 1947)

- appoggiatura (Vorhalt?): stressed dissonance: step or skip (more common than step, adds emphasis) from any direction into dissonance, resolved in second below or above. Dissonance on strong beat (requires metric position). Dissonance can be further stressed by a note duration longer than following note.
Torsten added: 'likes' to resolve in semitone, specially if upwards (Mozart..)

- passing note (Durchgang): diatonic or chormatic progression continued in same direction. Passing note always weak rhythmical quality -- even when occuring on first beat of measure (!!). According to Piston, notes are analysed as either appoggiatura or passing only depending on their rhythmic weight.
Exceptionally are 'passing notes' without any directly preceeding notes (this note sounded in other voice).

- suspension (vorbereiteter Vorhalt?): in classic case, a (comparatively long) harmonic note is 'tied over' -- the harmony changes and turnes the 'tied over' note into a non-harmonic note. Usually/often, this dissonance longs at least a beat. Suspensions are resolved in stepwise motion (usually downwards, upwards more likely in case of leading notes or chormatically raised notes). The suspension may resolved into next harmony (i.e. after the harmony it formed a dissonance in). The suspendend note is (in all cases?) longer than its successor.

- anticipation (Vorausnahme): dissonant note on easy beat preceeding the same consonant note pitch on strong beat (i.e. the harmony changed). The dissonant anticipation is shorter than its consonant successor.

- auxiliary (Nebennote): ornamental single note, leaving and returning to the same note by a second up or down. The harmony may meanwhile change.

- echappee/cambiata (Torsten: standardised case of ornamental resolution?): stepwise movement of melody 'ornamented' note between. Echappee: leaves first note by stepwise motion in opposite direction and 'resolves' by skip of third to destination. Cambiata: first note by skip of third in opposite direction and 'resolves' by step to destination. Echappee and cambiata are rhythmically weak.
Variants with more freedom: larger skip than third or all movements in same direction (quasi like passing note with skip). Combination to double auxiliary (or changing-tones): echappee and cambiata follow each other directly as two dissonances.

- ornamental resolution: (i) multiple 'standard' dissonances directly following each other. E.g., appoggiatura directly followed by cambiata (i.e. with delayed resolution). (ii) arbitrary consonant chord note between 'standard' dissonance treatment. E.g., before passing note skip to some other chord note and (possibly) skip back to actual passing note. (iii) a group of interpolated tones (Piston recomments studying Bach 'Italian Concerto')


Problem with specific non-harmonic pitches, especially appoggiatura, in this context: how to 'motivate' non-harmonic pitches. E.g. in case of melody harmonisation, obviously harmonic pitches followed by a passing note could be understood as non-harmonic pitches which resolve into a harmonic pitch..


Functor

Import

Export

Define

proc{GetInterval Note1 Note2 Interval}
Returns the absolute pitch interval (a FD int) between the note objects Note1 and Note2. Interval is implicitly declared a FD int.


proc{GetPcInterval Note1 Note2 PcInterval}
Returns the pitch class interval (a FD int) between the note objects Note1 and Note2. Interval is implicitly declared a FD int.


proc{ConstrainMaxIntervalR Note1 Note2 MaxInterval B}
B=1 <-> constraints the absolute pitch interval between Note1 and Note2 (note objects) to MaxInterval (an integer) at most.


fun{MakeIntervalConstraint Ratios}
Expects a list of ratios (pairs of ints) and returns a binary constraint {IsInRatios Interval B} with Interval (FD int) a pitch interval and B a 0/1 int.
B=1 <-> Interval reduced into a single octave is element in ratios, translated to pitch classes.


proc{MinCard Notes Card}
The cardiality of the set of pitchclasses of Notes (list of HS.score.note objects) is at least Card (FD int).


proc{GetFeature X Feat I}
Constraints the chord/scale X: the arbitrary (user defined) chord/scale database feature value at Feat is accessed/constrained to I (a FD int or FS var, depending on the feature). For instance, if X is a chord and the chord database defines a numeric 'dissonanceDegree' for each chord in the database, {GetFeature X 'dissonanceDegree' 2} constraints the index of X to point to a chord spec in the database which has a dissonanceDegree of 2.
NB. GetFeature employs a selection constrain: multiple applications of GetFeature with the same Feat will accessed/constrain the same value I with multiple selection constraint propagators, which should be avoided..

!! NB: does not work for intervalDB, because there is no interval ADT which defines getDB and getIndex for interval.


proc{UnequalParameter X Y Fn}
The chords/scales X and Y differ in the parameter/attribute accessed by Fn (a unary function or method). For instance, {UnequalParameter X Y getIndex} constrains the indices of X and Y to differ.


proc{UnequalParameterR X Y Fn B}
Reified version of DistinctParameter: B=1 <-> chords/scales X and Y differ in the parameter/attribute accessed by Fn.


proc{NeighboursWithUnequalParameter Xs Fn}
All successive chord/scale pairs in list Xs differ in the parameter/attribute accessed by Fn.


proc{Distinct X Y}
The chords/scales X and Y have either different indices or different transpositions or both.


proc{DistinctR X Y B}
B=1 <-> The chords/scales X and Y have either different indices or different transpositions or both.


proc{DistinctNeighbours Xs}
All successive chord/scale pairs in list Xs have either different indices or different transpositions or both.


proc{PairwiseDistinct Xs}
All chords/scales in list Xs are pairwise distinct, i.e. they have either different indices or different transpositions or both.


proc{ButNDistinct Xs N}
All but N (a FD int) chords/scales in list Xs are pairwise distinct, i.e. they have either different indices or different transpositions or both. That is, N=4 <-> four chords/scales are not unique in Xs (either all four are the same or two different chords/scales repeated).


proc{DistinctForN Xs N}
Xs (a list of chords/scales) is split into sublists of length N: in each sublist, all chords/scales are pairwise distinct.


proc{CommonPCs X Y}
Constraints the chords/scales X and Y to have at least 1 common pitch class.


proc{CommonPCs_Card X Y N}
N (an FD int) is the cardiality of the set of common pitch classes between the chords/scales X and Y.


proc{CommonPCsR X Y B}
Reified version of CommonPCs: B=1 <-> chords/scales X and Y have at least 1 common pitch class.


proc{NeighboursWithCommonPCs Xs}
Each successive chord/scale pair in list Xs has at least 1 common pitch class.
NB: The constraint introduces auxilary variables which possibly remain undetermined in the solution.


proc{ParameterDistance X Y Fn I}
Constraints the distance between the parameter/feature accessible with Fn of the chords/scales X and Y to I (a FD integer). For instance, if X and Y are chords and the chord database defines the numeric feature dissonanceDegree, the dissonanceDegree distance between X and Y is set to 1 by
{ParameterDistance X Y fun {$ X} {GetFeature X dissonanceDegree} end 1}


proc{ParameterDistanceR X Y Fn I B}
Reified version of ParameterDistance.


proc{LimitParameterDistanceOfNeighbours Xs Fn Max}
Limits the the distance between the parameter/feature accessible with Fn of the neigbouring chords/scales in Xs not to exceed Max (a FD integer, but in most cases an integer will do).

!! Better define LimitDistanceOfNeighbours as Pattern expecting list of FD ints..


fun{GetRootPCIntervals Chords}
Returns the list of PC intervals between the roots of Chords (list of chord objects).


fun{GetRootDegreeIntervals Chords MyScale}
Expects a list of Chords and a scale, and returns the absolute distances between the scale degrees of the chord roots (list of FD ints).


proc{SetBoundaryRoots Chords Args}
Sets the pitch classe of the first chord root in Chords (list of chords) and potentially constrains the pitch class interval between the first and last chord root.

Args:
'firstRoot' (default false): root of first chord (pc atom, see HS.pc). Ignored if set to false.
'firstToLastRootInterval' (default false): pc interval between first and last chord root (pc atom, e.g., 'C' is 0, or false). Ignored if set to false.
'lastRoot' (default false): root of last chord (pc atom, or false). Ignored if set to false.



proc{SetBoundaryTypes Chords Args}
Sets the types of the first and last chord in Chords (list of chords).

Args:
'firstType' / 'lastType' (default false): sets the type (index) of the first/last chord in Chords to the type specified, an atom (chord name specified in the database). Ignored if set to false.



proc{PcInChord PC C}
The pitch class PC (FD int) is included in the pitch class set of the chord C (a chord object).


proc{Cadence MyScale Chords}
Constraints the union of the pitch classes of Chords (a list of chord objects) to be the same set as the set of pitch classes of MyScale (a scale object). In other words, all chords only use scale tones (diatonic chords) and all scale tones are played. Also, the root of the last chord is constrained to the root of the scale.
In common usage, Chords has length three and is applied to the last three chords of a progression.
This constraint goes well with HS.rules.schoenberg.ascendingProgressionR and frieds (results in the common cadences for major in 12 ET, and plagal cadences for natural minor).

Note: this constraint implements a particular strict notion of a cadence, were all scale notes must sound. A less strict version would require that only pitch classes which distinguish a scale among all other likely scales are sufficient (e.g., the pitch classes G, B, and F are sufficient to distinguish C-major between all major scales). However, such a constraint is more context dependent and requires information on all scales which are alternatively possible (e.g., G, B, and F are not sufficient to confirm C-major if the scale could also be Dorian).


proc{DiatonicChord MyChord MyScale}
All pitch classes of MyChord are in MyScale (scale must of course not diatonic, procedure name uses the phrase "diatonic to" as a synonym for "belonging to").


proc{NoteInPCCollection MyNote MyPCCollection}
The pitch class of MyNote is in MyPCCollection (a chord or a scale).


proc{Modulation Chords OldScale NewScale Args}
Modulation constraints Chords (a list of chord objects) to perform a modulation. The modulation starts with Args.neutralLength chords that consist solely of pitch classes that are elements of both OldScale (scale object denoting scale before the modulation) and NewScale (scale object denoting scale after the modulation). For example, if As.neutralLength = 1 (the default) then the first chord of Chords will be a "neutral" chord.
The chord after the neutra chords is the modulation chord, which contains at least one pitch class of the new scale that was not part of the old scale. Optionally, a cadence is performed after the modulation chord (the modulation chord is part of the cadence) ending in a chord with the root of NewScale as its root.

Args:
neutralLength (int, default 0): number of neutral chords before the modulation chord.
cadence (int or false, default false): length of the cadence starting with the modulation chord. If false, no cadence constraint is applied.

NOTE: if OldScale and NewScale share only few pitch classes, then finding a neutral chord can be difficult...


proc{ResolveDissonances Chords Args}
Constraints that every chord in Chords which is not a consonant chord is resolved by an ascending chord progression. The last chord is implicitly constrained to be a consonant chord.
This rule is inspired by Schoenberg, note however that it is never given by Schoenberg (When introducing 7th-chords, Schoenberg requires they are resolved by root progression a fourth up. Besides that, Schoenberg primarily discusses treatment dissonant tone in chord: it descends by one step or is held over.).

Args:
'consonantChords' (default ['major' 'minor']): list of chord types (atoms of chord names or index integers) specifying which chords are considered consonant.

Note: this is an over-simplified dissonance resolution constraint.
- Dissonance preparation not supported (preparation only required in early section of Schoenberg's harmony, later he even skips into dissoance, though stepwise introduction of dissonance is preferred)
- Dissonances do not necessarily "fall" in resolution (which is OK in principle), but this resolution behaviour cannot be controlled



proc{NoParallels NotePairs}
Open and hidden parallel fifths or octaves are permitted: perfect consonances must not be reached by both voices in the same direction. NotePairs is a list of two-note-pairs. Each pair consists of consecutive notes in the same voice and NotePairs together are the simultaneous note pairs of all voices. In particular, the second element of each pair in NotePairs are all simultaneous notes -- if any of these form a perfect consonance, then the first notes of these pairs should not progress into these sim notes in the same direction.


proc{NoParallels2 Notes Args}
Neither open nor hidden parallel fifths or octaves are permitted: perfect consonances must not be reached by both voices in the same direction. Notes is the list of all notes to which the constraint is applied (e.g., the list of all notes in the score).

Args:
getPredecessor: unary function expecting a note and returning the preceding note in the same voice. Default:
fun {$ N} {N getPredecessor($ {N getTemporalAspect($)})} end


proc{NoParallel N1A#N1B N2A#N2B}
Neither open nor hidden parallels fifths or octaves are permitted: perfect consonances must not be reached by both voices in the same direction. The pairs N1A#N1B and N2A#N2B are pairs of consecutive melodic notes, whereas N1B and N2B are simultaneous notes.
NB: fourth is not considered perfect consonance by IsPerfectConsonanceR.


proc{IndexCardinality Chords N}
Sets the total number of different chord indices in all Chords (a list of chords or scale objects) to N (FD int).


proc{SetEachChordType Chords Types}
Expects a list of chords and a list of atoms specifying chord types (indices) by name (e.g. 'major') and sets the index of each chord to the union of these types.


proc{SetEachScaleType Scales Types}
Expects a list of scales and a list of atoms specifying scale types (indices) by name (e.g. 'major') and sets the index of each scale to the union of these types.


proc{RequireChordTypes Chords Types}
In union of all Chords (list of chords) all Types (chord names given as atom, e.g., 'major') are present.


proc{ExpressAllChordPCs MyChord}
The union of the pitch classes of all notes notes simultaneous to MyChord fully expresses the pitch class set of this chord (more pitch classes are possibly, but all chord pitch classes must be played).


proc{ExpressAllChordPCs_AtChordStart MyChord}
Like ExpressAllChordPCs, but browses warning is number of sim notes is insufficient for expressing all chord tones.
*/
NOTE: is it a good idea to have extra constraint for this?
proc {ExpressAllChordPCs_Warn MyChord}
thread % waits until sim notes are accessible
Ns = {MyChord getSimultaneousItems($ test:isNote)}
C_Card = {FD.decl}
PCs = {Map Ns fun {$ N} {N getPitchClass($)} end}
PCsFS = {GUtils.intsToFS PCs}
in
C_Card = {FS.card {MyChord getPitchClasses($)}}
if {Length Ns} >= C_Card then
{FS.subset {MyChord getPitchClasses($)} PCsFS}
else {Browse warn('not enough notes for expressing full chord')}
end
end
end
/** %% More strict variant of ExpressAllChordPCs: all pitch classes must sound when chord starts.


proc{ExpressAllChordPCs_AtChordEnd MyChord}
More strict variant of ExpressAllChordPCs: all pitch classes must sound when chord end.


proc{ExpressEssentialChordPCs MyChord}
The union of the pitch classes of all notes notes simultaneous to MyChord fully express at least all essential pitch classes of this chord.
NB: the the essential pitch classes must be defined with the feature essentialPitchClasses in the chord DB.

BUG: this constraint failed where ExpressAllChordPCs worked -- so there is likely a serious bug.


proc{ExpressEssentialPCs_AtChordStart MyChord}
More strict variant of ExpressEssentialChordPCs: all essential pitch classes must sound when chord starts.
Because constraint application is not delayed so long, this more strict version can actuallyt be more efficient.

BUG: this constraint failed where ExpressAllChordPCs worked -- so there is likely a serious bug.


proc{ClearHarmonyAtChordBoundaries Chords VoiceNotes}
Defines contrapuntal constraint which implements proper suspension and other things. Chords is a list of chord objects and VoiceNotes a list of note objects which all belong to a single voice. At the border between two chords, the last voice note simultaneous to the preceeding chord and the first note simultaneous to the succeeding chord, these two notes should not be both non-chord tones (note: these two notes can be the same or different score objects, and have the same or different pitches).
If the first note of a chord is a non-chord tone, then it should have the same pitch as the last of the previous chord. In other words: if a chord starts with a non-chord tone, then it must be a suspension (suspension are of course less clear in a solo line...).
NB: this constraint assumes that neighbouring chords differ (e.g., have a different root), otherwise it is too strict.
NB: this constraint does not define that non-chord tones are resolved stepwise, but it can be combined, e.g.., with ResolveNonharmonicNotes.

Internally, each chord accesses its first/last simultaneous note within VoiceNotes.


proc{OnlyOrnamentalDissonance_Durations Notes}
Allows only for 'ornamental' in contrast to 'emphasized' non-harmonic tones. This definition only takes note durations into account. It restricts that a non-harmonic note must be preceeded and followed by a note that is at least as long as the non-harmonic note itself.


proc{VoiceLeadingDistance Chord1 Chord2 N}
Harmonic constraint on directionless voice-leading distance N (FD int, measured in steps of the present equal temperament) between two chords Chord1 and Chord2. The distance N is the minimal sum of intervals between Chord1 and Chord2. The voice-leading distance is directionless in the sense that regardless whether a voice moves up or down, always the smaller interval is taken into account. The lower N, the more "smooth" is the harmonic progression (repetition is quasi most smooth).

Example (in 12 ET): {VoiceLeadingDistance C_Major Ab_Major} = 2
C->C=0 + E->Eb=1 + G->Ab=1, so the sum is 2

Note: Only the minimal intervals from all Chord2 pitch classes to Chord1 pitch classes are taking into account. Swapping the arguments can lead to different results: there may be pitch classes in Chord1 which are ignored as all pitch classes of Chord2 may be closer to some other pitch classes of Chord1.

Example: C-maj -> F#-maj = 4
C->C#=1, C->A#=2, G->F#=1 -- the E of C-maj is ignored in the computation

Note: relatively expensive constraint. Also, only effective after of both Chord1 and Chord2 are (mostly) determined.


fun{VoiceLeadingDistance_Percent Chord1 Chord2}
Like VoiceLeadingDistance, but returns a percentage value depending on the cardiality of both Chord1 and Chord2. 100 percent is the theoretical maximum that all intervals are halve octaves.


proc{SmallIntervalsInProgression Chord1 Chord2 Args N}
Harmonic constraint for creating chord progressions where many pitch classes change by small intervals. N (FD int, implicitly declared) is the number of pitch class intervals between Chord1 and Chord2 which are =< some maximal interval, typically a semitone. The higher N, the more smooth is the transition (for VoiceLeadingDistance it is the other way round!).

Args:
'maxInterval': the maximum size of the interval which counts into the percentage. Default is the septimal diatonic semitone (15#14, i.e. given as a pair of integers denoting a ratio).
'ignoreUnisons': if true (the default), unisons do not count into the percentage.

Examples:

{SmallIntervalProgression C_Major Ab_Major unit} = 2
Small intervals counting: E->Eb, G->Ab

{SmallIntervalProgression C_Major Ab_Major unit(ignoreUnisons:false)} = 3
Small intervals counting: C->C, E->Eb, G->Ab

Note: relatively expensive constraint. Also, only effective after of both Chord1 and Chord2 are (mostly) determined.


proc{SmallIntervalsInProgression_Percent Chord1 Chord2 Args Percent}
Like SmallIntervalsInProgression, but constraints the percentage depending on the cardiality of both Chord1 and Chord2. 100 percent denotes the cardiality of the chord with more notes, 0 percent means no note pair changes by a small interval.


proc{RestrictPitchDomain Notes MaxDom MinDom}
Expects a list of notes and two pitches MaxDom and MinDom (specified in the formats supported by HS.pitch, e.g., a pitch integer or an ET31 pitch specified like 'C'#4). These set the upper and lower pitch domain of all notes.


proc{IsStep Pitch1 Pitch2 MaxStep}
The interval between Pitch1 and Pitch2 is in [1, MaxStep].


proc{IsStepR Pitch1 Pitch2 MaxStep B}
In case B=1, the interval between Pitch1 and Pitch2 is in [1, MaxStep]. B is implicitly declared an 0/1 integer.


proc{ResolveStepwiseR Note Args B}
Constraints the interval between the pitches of Note and its successor to be in [0, MaxStep]. MaxStep defaults to 2 and can be set as optional feature of Args. Per default, the successor note is the successor of Note in the sequence of items contained in the temporal aspect of Note (i.e. the sequence/list returned by {GetTemporalAspectItems Note}). This setting can be changed with the optional Args feature getXs (e.g. to a function which does return the list of items recursively contained in the temporal aspect of the temporal aspect Note. Such setting would apply ResolveStepwiseR even across container boundaries when Note is the last element in its temporal aspect).
In case Note has no successor, B=0.

BTW: ResolveStepwiseR defines a dissonance treatment simplification. Most of the conventional non-harmonic note pitch conditions identified by Piston (see above) are permitted: appoggiatura, passing note, suspension, anticipation, auxiliary, and cambiata. Only the echappee is excluded by ResolveStepwiseR.
Nevertheless, the simplification ResolveStepwiseR allows also cases which are not permitted by the conventional non-harmonic note pitch treatment (e.g. an appoggiatura on an easy beat, or a long anticipation which preceeds a short note on an easy beat).
Problem: if any note (even regardless of metric position) can be understood as appoggiatura, the implicit harmony is easily misread.


proc{PassingNotePitches [Pitch1 Pitch2 Pitch3] MaxStep}
Constraints [Pitch1 Pitch2 Pitch3] such that Pitch2 forms a passing note pitch. The intervals between neighbouring pitches are in [1, MaxStep] (usually, MaxStep = PitchesPerOctave div 6) and the pitch sequence is either monotonically increasing or decreasing. All pitches are FD ints, MaxStep is int.
NB: for this rule, it is irrelevant whether any pitch is consonant, or dissonant and whether it is a chord pitch or not.


proc{PassingNotePitchesR [Pitch1 Pitch2 Pitch3] MaxStep B}
Reified version of PassingNotes (see above).
NB: Introduces a FD int which may not be determined (in case B=0).


proc{IsPassingNoteR Note Args B}
In case B=1, Note is a passing note between its predecessor and successor. Args is a record of optional arguments: maxStep (defaults to 2) and getXs, a unary function applied to Note returning the list of items in the melody including Note, defaults to the items in the temporal aspect of Note.
See also PassingNotePitches and PassingNotePitchesR above.
In case Note has no predecessor or successor, B=0.
BTW: IsPassingNoteR is a generic passing note definition which can be applied, e.g., to a melody across container boundaries (e.g. a melody consisting in motifs which consist in note sequences) by returning the list of notes in this melody from getXs.
NB: Predecessor and successor of Note must be notes as well!


proc{IsBetweenChordNotesR Note Args B}
In case B=1, both the predecessor and successor of Note return 1 (i.e. true) for the method isInChord (which means that both notes are harmonic notes: their pitch class is included in the pitch classes of their repsective chord). Args is a record with the optional argument getXs, a unary function applied to Note returning the list of items in the melody including Note, defaults to the items in the temporal aspect of Note.
In case Note has no predecessor or successor, B=0.


proc{IsAuxiliaryR Note Args B}
In case B=1, both the predecessor and successor of Note have the same pitch and the pitch of Note is only a step away.
Args is a record with the optional argument maxStep (defaults to 2) and getXs (a unary function applied to Note returning the list of items in the melody including Note, defaults to the items in the temporal aspect of Note). See also ResolveStepwiseR for an explaination of Args.
In case Note has no predecessor or successor, B=0.
NB: Predecessor and successor of Note must be notes as well!


proc{IsBetweenStepsR Note Args B}
In case B=1, both the pitches of the predecessor and successor are only a step away from Note's pitch.
Args is a record with the optional argument maxStep (defaults to 2) and getXs (a unary function applied to Note returning the list of items in the melody including Note, defaults to the items in the temporal aspect of Note). See also ResolveStepwiseR for an explaination of Args.
In case Note has no predecessor or successor, B=0.

BTW: This rule generalises passing note and auxiliary. Nevertheless, a further case is also permitted: pitch contour between three successive notes as for an auxiliary, but predecessor and successor have different pitches. For instance, in case maxStep=2, predecessor and successor differ by a semitone.
NB: Predecessor and successor of Note must be notes as well!


proc{ResolveNonharmonicNotesStepwise Notes Args}
Melodic constraint for list of Notes: non-chord tones are only permitted if they are reached and left by a step. The first and last element of Notes is constrained to a chord tone.

Args:
'maxInterval': an ratio spec for the maximum step-size permitted. Default is a septimal second (8#7).


proc{ClearDissonanceResolution VoiceNotes}
Variant of ResolveNonharmonicNotesStepwise which accesses the predecessor and sucessor note from a given note explicitly. Non-chord tones are only permitted for MyNote if they are reached and left by a step. If no predecessor or successor is accessible for MyNote, then it must be a chord tone.

Args:
'maxInterval': an integer specifying the maximum step-size permitted. Default is the interval corresponding to a septimal second (8/7).
'getPredecessor' and 'getSuccessor': unary function returning the predecessor/successor for the given note. Default are the methods getTemporalPredecessor/getTemporalSuccessor.
NOTE: If motifs are wrapped in containers, then the first (last) motif note has no predecessor (successor) and consequently must be a chord tone. This default behaviour can be changed using different 'getPredecessor' and 'getSuccessor' settings.
NOTE: the default 'getPredecessor' and 'getSuccessor' do allow for pauses between chord tones and non-harmonic tones. Classical music theory does this as well, but not completely unrestricted. Again, this can be changed by using different 'getPredecessor' and 'getSuccessor' settings.
*/
proc {ResolveNonharmonicNotesStepwise2 MyNote Args}
Defaults = unit(getPredecessor: fun {$ N}
X = {N getTemporalPredecessor($)}
in
replace X by comment if pauses should not occur between predecessor/successor and MyNote
X
if X == nil orelse {X isPause($)} orelse ({N getOffsetTime} >: 0) == 1
then nil
else X
end
end
getSuccessor: fun {$ N}
X = {N getTemporalSuccessor($)}
in
replace X by comment if pauses should not occur between predecessor/successor and MyNote
X
if X == nil orelse {X isPause($)} orelse ({X getOffsetTime} >: 0) == 1
then nil
else X
end
end

maxInterval: SeptimalSecond
)
As = {Adjoin Defaults Args}
/** %% B=1 <-> MyNote is entered and left by a step.
*/
proc {Aux MyNote B}
Pre = {As.getPredecessor MyNote}
Succ = {As.getSuccessor MyNote}
in
B = {FD.int 0#1} % needed?
if Pre \= nil andthen Succ \= nil
then
B = {FD.conj {ConstrainMaxIntervalR Pre MyNote As.maxInterval}
{ConstrainMaxIntervalR MyNote Succ As.maxInterval}}
else B=0 % otherwise always a consonance
end
end
in
thread % accessors block
{MyNote nonChordPCConditions([Aux])}
end
end




/** %% [contrapuntual constraint] If in one voice there occurs a non-chord tone followed by a chord tone (a dissonance resolution), then no other voice should obscure this resolution by a non-chord tone starting together with the tone resolving the dissonance. However, simultaneous dissonances can start more early or later.


proc{IntervalBetweenNonharmonicTonesIsConsonant Notes ConsonantIntervals}
[contrapuntual constraint] Constraints that all pairs of simultaneous non-harmonic tones (i.e. the inChordB parameter = 0) form consonant intervals among each other. Notes is the list of all notes which potentially are non-harmonic tones (e.g., all notes in the score). ConsonantIntervals is a FD int domain specification (e.g., a list of integers) which specifies the allowed intervals.


proc{MaxInterval Notes MaxInt}
Constraints that no pitch interval between consecutive Notes (list of note objects) exceeds MaxInt (FD int).


proc{MaxNonharmonicNoteSequence Notes N}
Restrict the number of consecutive non-harmonic Notes (list of note objects) to N a maximum. Non-harmonic notes are notes for which the method getInChordB returns 0 (i.e. false).


proc{MaxNonharmonicNotePercent Notes MaxPercent}
Restrict the maximum percentage of non-harmonic Notes (list of note objects) to MaxPercent.


proc{MaxRepetitions Notes N}
N specifies how many pitch repetitions occur at maximum between consecutive Notes (list of note objects), i.e. how many pitch intervals are 0. If N=0 then no repetitions are permitted.


proc{MinPercentSteps Notes MinPercent Args}
Constrains the interval between Notes (list of note objects): there are at least MinPercent steps. The optional argument 'step' sets the step size as a frequency ratio (default 8#7).


proc{Ballistic Pitches Args}
After a upward (downward) skip, either move into the same direction by a smaller (larger) skip or step, or move into the opposite direction. Pitches is a list of FD ints.

Args:
'maxStep' (default 8#7): maximal step size, specified as ratio (pair of integers).
'oppositeIsStep' (default false): if true and direction is changed after a skip, then this first interval into the opposite direction must be a step.



End