"""Patterns that are useful for constructing
scales and chords.
A scale is constructed with a note and one such pattern
(for example, C major consists of the note C as well as
the 2\\ :sup:`nd`, 4\\ :sup:`th`, 5\\ :sup:`th` ... and so on,
notes from C.) This pattern is the mode (or key) of the scale.
Whereas scales (see :mod:`scale`) are constructed by picking notes in a music
space, chords are (see :mod:`chord`) constructed by picking notes from a scale.
Both (:mod:`scale`) and (:mod:`chord`) can use constants defined
in this module.
"""
from collections import deque
from . import reach
from typing import Sequence
from . import Interval
# Construct modes of the major scale.
# Default shift to right
def _circular_shift(intervals: list[Interval],
shift_by: int) -> list[Interval]:
"""Shift :arg:`seq` to the right by
:arg:`shift_by` as if it were a cyclic buffer.
"""
return intervals[-shift_by:] + intervals[:-shift_by]
def _reduce_interval(intervals: list[Interval],
indices: list[int]) -> list[Interval]:
indices = [x - 1 for x in indices][1:]
assert len(indices) < len(intervals)
accumulator: int = 0
adjudicator: list[Interval] = []
indices = sorted(indices)
indices_poppable: deque[int] = deque(indices)
for i in range(len(intervals)):
if not indices_poppable:
adjudicator.append(accumulator)
break
accumulator += intervals[i]
if indices_poppable[0] == i:
adjudicator.append(accumulator)
accumulator = 0
indices_poppable.popleft()
return adjudicator
[docs]
def pentatonic_major(hep: list[Interval]) -> list[Interval]:
assert len(hep) == 7
return _reduce_interval(hep, [x - 1 for x in [1, 2, 3, 5, 6]])
[docs]
def pentatonic_minor(hep: list[Interval]) -> list[Interval]:
assert len(hep) == 7
return _reduce_interval(hep, [x - 1 for x in [1, 3, 4, 5, 7]])
def _add_flat_at(scale: list[Interval], index: int) -> list[Interval]:
"""Add a flat at the :arg:`index`\\ :sup:`th`
position of :arg:`scale`.
Note that because indices in this code base
are 0-based, use :code:`index=n-1` to add a
flat of the n\\ :sup:`th` note.
Actually I'm not sure if this is how things work.
Trial and error be your friend, or something.
"""
index = index - 1
return scale[:index] \
+ [scale[index] - 1, 1] \
+ scale[index + 1:]
[docs]
def blues_major(hep: list[Interval]) -> list[Interval]:
# Maybe it is correct? The warning message is annoying
# though, so I removed it.
# print("Warning: The correctness of"
# " `blues_major` has not been verified.")
return _add_flat_at(pentatonic_major(hep), 2)
[docs]
def blues_minor(hep: list[Interval]) -> list[Interval]:
# Maybe it is correct? The warning message is annoying
# though, so I removed it.
# print("Warning: The correctness of"
# " blues_minor` has not been verified.")
return _add_flat_at(pentatonic_minor(hep), 3)
[docs]
def harmonic_minor(minor_scale: list[Interval]) -> list[Interval]:
return [*minor_scale[:-2],
minor_scale[-2] + 1,
*minor_scale[-1:]]
[docs]
def melodic_minor(minor_scale: list[Interval]) -> list[Interval]:
return [*minor_scale[:-3],
minor_scale[-3] + 1,
*minor_scale[-2:]]
def _count_triad(root: str,
interval_list: Sequence[str]) -> list[str]:
global interval
return [reach(root, interval)
for interval in interval_list]
def _count_triad_major(root: str) -> list[str]:
return _count_triad(root, ["perfect 8", "major 3", "perfect 5"])
def _count_triad_minor(root: str) -> list[str]:
return _count_triad(root, ["perfect 8", "minor 3", "perfect 5"])
def _count_triad_augmented(root: str) -> list[str]:
return _count_triad(root, ["perfect 8", "major 3", "augmented 5"])
def _count_triad_diminished(root: str) -> list[str]:
return _count_triad(root, ["perfect 8", "minor 3", "diminished 5"])
MAJOR: list[Interval] = [2, 2, 1, 2, 2, 2, 1]
MINOR: list[Interval] = [2, 1, 2, 2, 1, 2, 2]
MINOR_NATURAL = MINOR
MINOR_HARMONIC: list[Interval] = [2, 1, 2, 2, 1, 3, 1]
MINOR_MELODIC: list[Interval] = [2, 1, 2, 2, 2, 2, 2]
IONIAN: list[Interval] = _circular_shift(MAJOR, 0)
DORIAN: list[Interval] = _circular_shift(MAJOR, -1)
PHRYGIAN: list[Interval] = _circular_shift(MAJOR, -2)
LYDIAN: list[Interval] = _circular_shift(MAJOR, -3)
MIXOLYDIAN: list[Interval] = _circular_shift(MAJOR, -4)
AEOLIAN: list[Interval] = _circular_shift(MAJOR, -5)
LOCRIAN: list[Interval] = _circular_shift(MAJOR, -6)
DIMINISHED_WHOLE_HALF: list[Interval] = [2, 1] * 4
DIMINISHED_HALF_WHOLE: list[Interval] = [1, 2] * 4
AUGMENTED: list[Interval] = [3, 1, 3, 1, 3, 1]
MAJOR_PENTATONIC = pentatonic_major(MAJOR)
MINOR_PENTATONIC = pentatonic_minor(MINOR)
MAJOR_BLUES = blues_major(MAJOR)
MINOR_BLUES = blues_minor(MINOR)
SEVENTH_AUGMENTED = [0, 4, 4, 2]
SEVENTH_DIMINISHED = [0, 3, 3, 3]
SEVENTH_MAJOR = [0, 4, 3, 4]
SEVENTH_MINOR = [0, 3, 4, 3]
TRIAD = [0, 4, 3]