Harmonia.Voicing
- Package
- purescript-harmonia
- Repository
- afcondon/purescript-harmonia
Harmonia.Voicing — concrete pitch & voice leading.
Voicings carry a chord's pitch classes at concrete octaves — ordered low
to high, as MIDI note numbers. The lift
closeVoicing :: { centre } -> Chord -> Voicing produces a default
close-position voicing at the given octave; everything else is a
Voicing -> Voicing transformation that composes through ordinary
function composition.
A Selector carves a sub-chord out of an existing chord or voicing — by
position in the sorted pitch-class array (for Chord) or by position in the
low-to-high voicing (for Voicing). The vocabulary is small: TakeLow /
TakeHigh / TakeRange / TakeIndices / TakeEvery / DropS.
voiceLead / enumerateVoicings move between chords by minimum total
semitone motion; play realises a whole Progression with voice-leading
carry-through. Pure Prelude/Data.*; depends only on Harmonia.Chord.
#Voicing Source
newtype VoicingA voicing is an ordered array of MIDI note numbers, sorted
ascending (low to high). Each element is a concrete realisable
note; the octave is implicit in the number (ndiv12).
MIDI convention: middle C (C4) is 60; octave 4 spans 60..71.
Constructors
Instances
#closeVoicing Source
closeVoicing :: { centre :: Int } -> Chord -> VoicingProduce a default close-position voicing of a chord, placed so all notes sit in the named octave. Chord pitch classes are played in ascending pitch-class order at the centre octave.
closeVoicing { centre: 4 } (Chord [0, 4, 7]) = Voicing [60, 64, 67]
Doesn't read slash info from the source DegreeChord — the slash
was baked into the Chord's pitch-class set at realize time and
the bass-below-other-notes arrangement is the caller's choice
via spread or octave-shifts.
#quartal Source
quartal :: Voicing -> VoicingRestack the voicing's pitch classes in cycle-of-4ths order from the bottom note, placing each subsequent note at the lowest octave giving at least a perfect-4th interval (5 semitones) above the previous. Quartal voicings sound "open" and modal; works well on chords that contain a 4th-stack (sus4, m11, jazz quartals), less well on pure triads where the natural intervals are 3rds.
quartal (Voicing [60, 65, 70, 67]) = Voicing [60, 65, 70, 79]
(sus chord C F Bb G → C F Bb G5; G placed above Bb)
#VoicingStrategy Source
type VoicingStrategy = Voicing -> VoicingA voicing strategy is a transformation on a voicing. Strategies compose through ordinary function composition.
#Selector Source
data SelectorA Selector carves a sub-chord out of a chord or voicing. Output is the same type as input, so selectors are composable and feed back into the Notation fabric like any other chord-shaped value.
Constructors
Instances
#takeVoicing Source
takeVoicing :: Selector -> Voicing -> VoicingApply a selector to a Voicing. Positions are interpreted against the low-to-high order; the bottom voice is position 0.
#Progression Source
type Progression = Array DegreeChordA progression is an ordered list of chord recipes.
#voiceLead Source
voiceLead :: Voicing -> Chord -> VoicingVoice-lead from the current voicing into the next chord. Returns
a voicing of nextChord whose notes are as close as possible to
currentVoicing — common pitch classes stay at the same MIDI
number, non-common ones move to the nearest octave.
Implementation: enumerate every permutation of the next chord's distinct pitch classes (factorial in chord size — fine for ≤7 voices), pair each current voice with one PC via nearest-octave placement, score by total |motion|, return the minimum.
If the current voicing and next chord have different sizes, falls
back to closeVoicing centred on the current voicing's bottom
octave. Voice-counts-changing-mid-progression is V-D territory.
#enumerateVoicings Source
enumerateVoicings :: Voicing -> Chord -> Array (Tuple Voicing Int)All distinct (voicing, motion) candidates for voice-leading from
a current voicing into a next chord, sorted by motion ascending.
The smallest-motion result is voiceLead's output; subsequent
entries are progressively less smooth alternatives.
Same size constraint as voiceLead — returns a single fallback
entry on size mismatch.
#play Source
play :: Key -> VoicingStrategy -> Progression -> Array Voicingplay with the default centre octave 4 (middle C area).
#playFrom Source
playFrom :: Int -> Key -> VoicingStrategy -> Progression -> Array VoicingGlue: realise a progression in a key, applying the voicing strategy to the first chord and voice-leading every subsequent one from the previous voicing. Returns one Voicing per DegreeChord in the progression.
centre is the octave the first voicing centres on; subsequent
voicings drift via voice-leading. See play for a centre-4
default.
- Modules
- Harmonia.
Chord - Harmonia.
Voicing