Karya, built on 2018-05-31T02:46:59 (patch 0a1a35479c514820d77330ae8a978975ba22a47a)

Safe HaskellNone




Functions to manipulate pitches in scales according to the rules of standard western music theory.

Pitches are represented as Pitch.Pitches and Pitch.Degrees. They're generalized to work with any number of Pitch.PitchClasses, but since each scale carries an implied key Layout it can only handle pitch classes in a certain range. Internally there are no checks that the pitch class is in range, so the range has to be checked on parse or show. Parsing and showing is handled in Derive.Scale.TheoryFormat.

       db      eb  fb      gb      ab      bb  cb
       c      e      g      b#
   c       d       e   f       g       a       b   c
   |   |   |   |   |   |   |   |   |   |   |   |   |


piano_layout :: Layout Source #

The layout of keys on everyone's favorite boxed harp.

NoteNumber diatonic transposition

diatonic_to_chromatic :: Key -> Pitch.Degree -> Double -> Double Source #

Convert a fractional number of diatonic steps to chromatic steps.

symbolic transposition

transpose_diatonic :: Key -> Step -> Pitch.Pitch -> Pitch.Pitch Source #

Transpose a pitch diatonically. If the key is diatonic (i.e. there is a 1:1 relationship between note letters and scale degrees), then this will increment or decrement the note letters by the number of steps and adjust the accidentals based on the key signature. Otherwise (i.e. for scales like whole-tone or octatonic), it will figure out the number of chromatic steps to transpose and act like transpose_chromatic.

transpose_chromatic :: Key -> Pitch.Semi -> Pitch.Pitch -> Pitch.Pitch Source #

Chromatic transposition. Try to pick a spelling that makes sense for the given key.


enharmonics_of :: Layout -> Pitch.Pitch -> [Pitch.Pitch] Source #

Enharmonics of a pitch.

This choses the next highest enharmonic until it wraps around, so if you repeatedly pick the first one you'll cycle through them all.

semis_to_pitch :: Key -> Pitch.Semi -> Pitch.Pitch Source #

Convert an absolute semitones value to a pitch. This is a bit complicated because it wants to find the best spelling for the given key.

pick_enharmonic :: Key -> Pitch.Pitch -> Pitch.Pitch Source #

Pick the most sensible enharmonic for the given pitch.

TODO I reduce to semis and then pick an enharmonic, so 5b# becomes 6c. But if they asked for 5b# they should get it.

semis_to_pitch_sharps :: Layout -> Pitch.Semi -> Pitch.Pitch Source #

Like semis_to_pitch, but only emits sharps, so it doesn't require a key.

semis_to_nn :: Pitch.Semi -> Int Source #

Convert Semis to integral NNs. This is only valid for 12TET, which is the only scale where Semis correspond directly to NNs.

It doesn't return Pitch.NoteNumber because these values are specifically integral.

NOTE [middle-c] Middle C is 5 octaves above NN 0, but is conventially called 4c. Therefore, a Pitch with octave 0 actually starts at NN 12 (in 12TET), and I have to add an octave when converting from NNs and subtract an octave when converting from NNs.

Previously I considered the octave offset a part of formatting, and added an octave in p_octave and subtracted an octave in show_octave. But I was unsatisfied because it applied to all scales, and it seemed confusing to ask for a Pitch with octave 4 and get a note with octave 3. TODO maybe the add/subtract octave should just go in TheoryFormat.absolute_c?


data Key Source #

A Key is a scale along with a tonic Pitch.

There's a distinction between "diatonic" and "chromatic" keys. It's not really standard terminology, but within this module I call scales with a 1:1 Pitch.PitchClass to Degree mapping "diatonic", and the ones without "chromatic". That's because diatonic transposition for the former kind of scale is defined in terms of pitch classes, regardless of what accidentals the Pitch.Degree may have, but the latter kind of scale must resort to chromatic transposition, losing the spelling of the original note. Ultimately there is a tension between diatonic and chromatic systems.

Eq Key # 
Instance details

Defined in Derive.Scale.Theory


(==) :: Key -> Key -> Bool #

(/=) :: Key -> Key -> Bool #

Show Key # 
Instance details

Defined in Derive.Scale.Theory


showsPrec :: Int -> Key -> ShowS #

show :: Key -> String #

showList :: [Key] -> ShowS #

Pretty.Pretty Key # 
Instance details

Defined in Derive.Scale.Theory

key :: Pitch.Degree -> Text -> [Pitch.Semi] -> Layout -> Key Source #

Make a Key given intervals and a layout. If the number of intervals are equal to the number of intervals in the layout, the scale is considered diatonic and will get a Signature.

accidentals_at_pc :: Key -> Pitch.PitchClass -> Pitch.Accidentals Source #

The number of accidentals in the key signature at the given pitch class.

type Signature = Vector.Vector Pitch.Accidentals Source #

Map from a Step to the number of sharps or flats at that Step.

type Intervals = Vector.Vector Pitch.Semi Source #

Semitones between each scale degree.

contains_degree :: Intervals -> Pitch.Degree -> Bool Source #

True if the degree exists as its own key in the layout.

For a relative scale, the Intervals should be from key_intervals, which considers that the tonic is shifted to PC 0. For an absolute scale, the keyboard never shifts, so use layout_intervals.

data Layout Source #

A Layout represents the configuration of white and black keys.

Eq Layout # 
Instance details

Defined in Derive.Scale.Theory


(==) :: Layout -> Layout -> Bool #

(/=) :: Layout -> Layout -> Bool #

Show Layout # 
Instance details

Defined in Derive.Scale.Theory