Safe Haskell | Safe-Inferred |
---|
Functions to manipulate pitches in scales according to the rules of standard western music theory.
Pitches are represented as Pitch.Pitch
es and Pitch.Degree
s. They're
generalized to work with any number of Pitch.PitchClass
es, 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# d# e# f# g# a# b# c d e f g a b c | | | | | | | | | | | | |
Synopsis
- piano_intervals :: [Pitch.Semi]
- piano_layout :: Layout
- diatonic_layout :: Pitch.PitchClass -> Layout
- diatonic_to_chromatic :: Key -> Pitch.Degree -> Double -> Pitch.FSemi
- transpose_diatonic :: Key -> Step -> Pitch.Pitch -> Pitch.Pitch
- transpose_chromatic :: Key -> Pitch.Semi -> Pitch.Pitch -> Pitch.Pitch
- enharmonics_of :: Layout -> Pitch.Pitch -> [Pitch.Pitch]
- pitch_to_semis :: Layout -> Pitch.Pitch -> Pitch.Semi
- degree_to_semis :: Layout -> Pitch.Degree -> Pitch.Semi
- semis_to_pitch :: Key -> Pitch.Semi -> Pitch.Pitch
- pick_enharmonic :: Key -> Pitch.Pitch -> Pitch.Pitch
- semis_to_pitch_sharps :: Layout -> Pitch.Semi -> Pitch.Pitch
- semis_to_nn :: Pitch.Semi -> Int
- fsemis_to_nn :: Pitch.FSemi -> Pitch.NoteNumber
- nn_to_semis :: Int -> Pitch.Semi
- data Key
- key :: Pitch.Degree -> Text -> [Pitch.Semi] -> Layout -> Key
- accidentals_at_pc :: Key -> Pitch.PitchClass -> Pitch.Accidentals
- type Signature = Vector.Vector Pitch.Accidentals
- type Intervals = Vector.Vector Pitch.Semi
- layout :: [Pitch.Semi] -> Layout
- layout_pc_per_octave :: Layout -> Pitch.PitchClass
- layout_semis_per_octave :: Layout -> Pitch.Semi
- contains_degree :: Intervals -> Pitch.Degree -> Bool
- data Layout
constants
piano_intervals :: [Pitch.Semi] Source #
piano_layout :: Layout Source #
The layout of keys on everyone's favorite boxed harp.
NoteNumber diatonic transposition
diatonic_to_chromatic :: Key -> Pitch.Degree -> Double -> Pitch.FSemi 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.
input
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.
pitch_to_semis :: Layout -> Pitch.Pitch -> Pitch.Semi Source #
degree_to_semis :: Layout -> Pitch.Degree -> Pitch.Semi Source #
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?
nn_to_semis :: Int -> Pitch.Semi Source #
key
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.
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.
layout :: [Pitch.Semi] -> Layout Source #
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
.
A Layout represents the configuration of white and black keys.