mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 23:00:20 +00:00
Add Arabic and Japanese systems, guitar tuning presets, logo, systems docs
New systems:
- Arabic (Maqam): 10 maqamat (ajam, hijaz, nahawand, nikriz, saba, etc.)
with Arabic solfège tone names (Do, Re, Mi, Fa, Sol, La, Si)
- Japanese: 6 pentatonic scales (hirajoshi, in, yo, iwato, kumoi, insen)
and 2 heptatonic scales (ritsu, ryo)
Fretboard improvements:
- Fretboard.guitar() now accepts tuning parameter
- Built-in tunings: standard, drop d, open g, open d, open e, open a,
dadgad, half step down
- Custom tuning via tuple: Fretboard.guitar(("E4", "B3", ...))
- Fretboard.bass(five_string=True) for 5-string bass
Docs:
- Add Musical Systems guide page with all 4 systems
- Add logo to docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
@@ -35,5 +35,6 @@ exclude_patterns = ["_build"]
|
||||
|
||||
html_theme = "alabaster"
|
||||
html_title = "PyTheory"
|
||||
html_logo = "_static/logo.png"
|
||||
html_static_path = ["_static"]
|
||||
html_extra_path = ["CNAME"]
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
Musical Systems
|
||||
===============
|
||||
|
||||
PyTheory supports four musical systems, each with its own tone names
|
||||
and scale patterns.
|
||||
|
||||
Western
|
||||
-------
|
||||
|
||||
The standard 12-tone equal temperament system with major/minor scales
|
||||
and all seven modes.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pytheory import TonedScale
|
||||
|
||||
c = TonedScale(tonic="C4")
|
||||
c["major"].note_names
|
||||
# ['C', 'D', 'E', 'F', 'G', 'A', 'B', 'C']
|
||||
|
||||
c["dorian"].note_names
|
||||
# ['C', 'D', 'D#', 'F', 'G', 'A', 'A#', 'C']
|
||||
|
||||
**Scales:** major, minor, harmonic minor, ionian, dorian, phrygian,
|
||||
lydian, mixolydian, aeolian, locrian, chromatic
|
||||
|
||||
Indian Classical (Hindustani)
|
||||
-----------------------------
|
||||
|
||||
The Hindustani system uses **swaras** (Sa, Re, Ga, Ma, Pa, Dha, Ni) and
|
||||
organizes scales into **thaats** — the 10 parent scales from which ragas
|
||||
are derived.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pytheory import TonedScale
|
||||
from pytheory.systems import SYSTEMS
|
||||
|
||||
sa = TonedScale(tonic="Sa4", system=SYSTEMS["indian"])
|
||||
|
||||
sa["bilawal"].note_names # = major scale
|
||||
# ['Sa', 'Re', 'Ga', 'Ma', 'Pa', 'Dha', 'Ni', 'Sa']
|
||||
|
||||
sa["bhairav"].note_names # unique to Indian music
|
||||
# ['Sa', 'komal Re', 'Ga', 'Ma', 'Pa', 'komal Dha', 'Ni', 'Sa']
|
||||
|
||||
sa["todi"].note_names
|
||||
# ['Sa', 'komal Re', 'komal Ga', 'tivra Ma', 'Pa', 'komal Dha', 'Ni', 'Sa']
|
||||
|
||||
**Thaats:** bilawal, khamaj, kafi, asavari, bhairavi, kalyan, bhairav,
|
||||
poorvi, marwa, todi
|
||||
|
||||
**Swara notation:**
|
||||
|
||||
- Uppercase = shuddha (natural): Sa, Re, Ga, Ma, Pa, Dha, Ni
|
||||
- ``komal`` prefix = flat: komal Re, komal Ga, komal Dha, komal Ni
|
||||
- ``tivra`` prefix = sharp: tivra Ma
|
||||
|
||||
Arabic Maqam
|
||||
------------
|
||||
|
||||
The Arabic system uses **solfège-based names** (Do, Re, Mi, Fa, Sol, La, Si)
|
||||
and organizes scales into **maqamat** (plural of maqam).
|
||||
|
||||
.. note::
|
||||
|
||||
True maqam music uses quarter-tones that cannot be represented in
|
||||
12-tone equal temperament. These scales are the closest 12-TET
|
||||
approximations.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pytheory import TonedScale
|
||||
from pytheory.systems import SYSTEMS
|
||||
|
||||
do = TonedScale(tonic="Do4", system=SYSTEMS["arabic"])
|
||||
|
||||
do["ajam"].note_names # = major scale
|
||||
# ['Do', 'Re', 'Mi', 'Fa', 'Sol', 'La', 'Si', 'Do']
|
||||
|
||||
do["hijaz"].note_names # characteristic augmented 2nd
|
||||
# ['Do', 'Reb', 'Mi', 'Fa', 'Sol', 'Solb', 'Sib', 'Do']
|
||||
|
||||
do["nikriz"].note_names
|
||||
# ['Do', 'Re', 'Mib', 'Fa#', 'Sol', 'La', 'Sib', 'Do']
|
||||
|
||||
**Maqamat:** ajam, nahawand, kurd, hijaz, nikriz, bayati, rast, saba,
|
||||
sikah, jiharkah
|
||||
|
||||
Japanese
|
||||
--------
|
||||
|
||||
The Japanese system uses Western note names with traditional pentatonic
|
||||
and heptatonic scales from Japanese music.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pytheory import TonedScale
|
||||
from pytheory.systems import SYSTEMS
|
||||
|
||||
c = TonedScale(tonic="C4", system=SYSTEMS["japanese"])
|
||||
|
||||
c["hirajoshi"].note_names # most iconic Japanese scale
|
||||
# ['C', 'D', 'D#', 'G', 'G#', 'C']
|
||||
|
||||
c["in"].note_names # Miyako-bushi, used in koto music
|
||||
# ['C', 'C#', 'F', 'G', 'G#', 'C']
|
||||
|
||||
c["yo"].note_names # folk music scale
|
||||
# ['C', 'D', 'F', 'G', 'A#', 'C']
|
||||
|
||||
c["ritsu"].note_names # gagaku court music (= Dorian)
|
||||
# ['C', 'D', 'D#', 'F', 'G', 'A', 'A#', 'C']
|
||||
|
||||
**Pentatonic scales:** hirajoshi, in, yo, iwato, kumoi, insen
|
||||
|
||||
**Heptatonic scales:** ritsu, ryo
|
||||
|
||||
Cross-System Comparison
|
||||
-----------------------
|
||||
|
||||
Since all systems use 12-tone equal temperament, equivalent scales
|
||||
produce the same pitches:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pytheory import TonedScale, Tone
|
||||
from pytheory.systems import SYSTEMS
|
||||
|
||||
# These are all the same scale with different names
|
||||
western = TonedScale(tonic="C4")["major"]
|
||||
indian = TonedScale(tonic="Sa4", system=SYSTEMS["indian"])["bilawal"]
|
||||
arabic = TonedScale(tonic="Do4", system=SYSTEMS["arabic"])["ajam"]
|
||||
|
||||
# Same pitches
|
||||
c4 = Tone.from_string("C4", system="western")
|
||||
sa4 = Tone.from_string("Sa4", system="indian")
|
||||
do4 = Tone.from_string("Do4", system="arabic")
|
||||
|
||||
c4.frequency # 261.63
|
||||
sa4.frequency # 261.63
|
||||
do4.frequency # 261.63
|
||||
@@ -31,6 +31,7 @@ Work with tones, scales, chords, and fretboards using a clean, Pythonic API.
|
||||
guide/scales
|
||||
guide/chords
|
||||
guide/fretboard
|
||||
guide/systems
|
||||
guide/playback
|
||||
|
||||
.. toctree::
|
||||
|
||||
@@ -38,6 +38,36 @@ TONES = {
|
||||
("Pa",), # G — pancham
|
||||
("komal Dha",), # Ab — komal dhaivat
|
||||
],
|
||||
# Arabic maqam system — Arabic solfège names.
|
||||
"arabic": [
|
||||
("La",), # A
|
||||
("Sib",), # Bb — Si bemol
|
||||
("Si",), # B
|
||||
("Do",), # C
|
||||
("Reb",), # Db — Re bemol
|
||||
("Re",), # D
|
||||
("Mib",), # Eb — Mi bemol
|
||||
("Mi",), # E
|
||||
("Fa",), # F
|
||||
("Fa#",), # F#
|
||||
("Sol",), # G
|
||||
("Solb",), # Ab — Sol bemol
|
||||
],
|
||||
# Japanese system — uses Western names; scales are the unique part.
|
||||
"japanese": [
|
||||
("A",),
|
||||
("A#", "Bb"),
|
||||
("B",),
|
||||
("C",),
|
||||
("C#", "Db"),
|
||||
("D",),
|
||||
("D#", "Eb"),
|
||||
("E",),
|
||||
("F",),
|
||||
("F#", "Gb"),
|
||||
("G",),
|
||||
("G#", "Ab"),
|
||||
],
|
||||
}
|
||||
|
||||
DEGREES = {
|
||||
@@ -61,6 +91,24 @@ DEGREES = {
|
||||
("nishad", ()), # Ni — 7th
|
||||
("saptak", ()), # Sa — octave
|
||||
],
|
||||
"arabic": [
|
||||
("qarar", ()), # 1st — root
|
||||
("nawa", ()), # 2nd
|
||||
("thalth", ()), # 3rd
|
||||
("arba", ()), # 4th
|
||||
("khamis", ()), # 5th
|
||||
("sadis", ()), # 6th
|
||||
("sabi", ()), # 7th
|
||||
("jawab", ()), # octave
|
||||
],
|
||||
"japanese": [
|
||||
("ichi", ()), # 1st
|
||||
("ni", ()), # 2nd
|
||||
("san", ()), # 3rd
|
||||
("shi", ()), # 4th
|
||||
("go", ()), # 5th
|
||||
("roku", ()), # 6th (pentatonic scales skip some)
|
||||
],
|
||||
}
|
||||
|
||||
SCALES = {
|
||||
@@ -132,6 +180,80 @@ INDIAN_SCALES = {
|
||||
}
|
||||
}
|
||||
|
||||
# Arabic maqam scales (12-TET approximations).
|
||||
# True maqam uses quarter-tones; these are the closest 12-tone equivalents.
|
||||
ARABIC_SCALES = {
|
||||
12: {
|
||||
"chromatic": (12, {}),
|
||||
"maqam": [
|
||||
7,
|
||||
{
|
||||
# Ajam = Western major
|
||||
"ajam": {"intervals": (2, 2, 1, 2, 2, 2, 1)},
|
||||
# Nahawand = Western harmonic minor
|
||||
"nahawand": {"intervals": (2, 1, 2, 2, 1, 3, 1)},
|
||||
# Kurd = Western Phrygian
|
||||
"kurd": {"intervals": (1, 2, 2, 2, 1, 2, 2)},
|
||||
# Hijaz — augmented 2nd between 2nd and 3rd degrees
|
||||
"hijaz": {"intervals": (1, 3, 1, 2, 1, 2, 2)},
|
||||
# Nikriz — augmented 2nd between 3rd and 4th
|
||||
"nikriz": {"intervals": (2, 1, 3, 1, 2, 1, 2)},
|
||||
# Bayati (12-TET approx) — true bayati has quarter-flat 2nd
|
||||
"bayati": {"intervals": (1, 2, 2, 2, 1, 2, 2)},
|
||||
# Rast (12-TET approx) — true rast has quarter-flat 3rd and 7th
|
||||
"rast": {"intervals": (2, 1, 2, 2, 2, 1, 2)},
|
||||
# Saba (12-TET approx) — true saba has quarter-flat 2nd
|
||||
"saba": {"intervals": (1, 2, 1, 3, 1, 2, 2)},
|
||||
# Sikah (12-TET approx) — true sikah starts on quarter-flat
|
||||
"sikah": {"intervals": (1, 2, 2, 2, 1, 2, 2)},
|
||||
# Jiharkah
|
||||
"jiharkah": {"intervals": (2, 2, 1, 2, 2, 1, 2)},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
# Japanese pentatonic scales.
|
||||
JAPANESE_SCALES = {
|
||||
12: {
|
||||
"chromatic": (12, {}),
|
||||
"pentatonic": [
|
||||
5,
|
||||
{
|
||||
# Hirajoshi — the most well-known Japanese scale
|
||||
# C D Eb G Ab
|
||||
"hirajoshi": {"intervals": (2, 1, 4, 1, 4)},
|
||||
# In (Miyako-bushi) — used in koto music
|
||||
# C Db F G Ab
|
||||
"in": {"intervals": (1, 4, 2, 1, 4)},
|
||||
# Yo — folk music scale
|
||||
# C D F G Bb
|
||||
"yo": {"intervals": (2, 3, 2, 3, 2)},
|
||||
# Iwato — dark, dissonant pentatonic
|
||||
# C Db F Gb Bb
|
||||
"iwato": {"intervals": (1, 4, 1, 4, 2)},
|
||||
# Kumoi — similar to minor pentatonic
|
||||
# C D Eb G A
|
||||
"kumoi": {"intervals": (2, 1, 4, 2, 3)},
|
||||
# Insen — modern Japanese scale
|
||||
# C Db F G Bb
|
||||
"insen": {"intervals": (1, 4, 2, 3, 2)},
|
||||
},
|
||||
],
|
||||
"heptatonic": [
|
||||
7,
|
||||
{
|
||||
# Ritsu — gagaku court music scale
|
||||
# C D Eb F G A Bb (= Dorian)
|
||||
"ritsu": {"intervals": (2, 1, 2, 2, 2, 1, 2)},
|
||||
# Ryo — gagaku court music scale
|
||||
# C D E F# G A B (= Lydian)
|
||||
"ryo": {"intervals": (2, 2, 2, 1, 2, 2, 1)},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
SYSTEMS = NotImplemented
|
||||
|
||||
# Modes are rotations of the major scale pattern.
|
||||
|
||||
+34
-20
@@ -104,29 +104,43 @@ class Fretboard:
|
||||
def __len__(self):
|
||||
return len(self.tones)
|
||||
|
||||
@classmethod
|
||||
def guitar(cls):
|
||||
"""Standard guitar tuning (E4 B3 G3 D3 A2 E2)."""
|
||||
from .tones import Tone
|
||||
return cls(tones=[
|
||||
Tone.from_string("E4", system="western"),
|
||||
Tone.from_string("B3", system="western"),
|
||||
Tone.from_string("G3", system="western"),
|
||||
Tone.from_string("D3", system="western"),
|
||||
Tone.from_string("A2", system="western"),
|
||||
Tone.from_string("E2", system="western"),
|
||||
])
|
||||
TUNINGS = {
|
||||
"standard": ("E4", "B3", "G3", "D3", "A2", "E2"),
|
||||
"drop d": ("E4", "B3", "G3", "D3", "A2", "D2"),
|
||||
"open g": ("D4", "B3", "G3", "D3", "G2", "D2"),
|
||||
"open d": ("D4", "A3", "F#3", "D3", "A2", "D2"),
|
||||
"open e": ("E4", "B3", "G#3", "E3", "B2", "E2"),
|
||||
"open a": ("E4", "C#4", "A3", "E3", "A2", "E2"),
|
||||
"dadgad": ("D4", "A3", "G3", "D3", "A2", "D2"),
|
||||
"half step down": ("D#4", "A#3", "F#3", "C#3", "G#2", "D#2"),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def bass(cls):
|
||||
"""Standard bass guitar tuning (G2 D2 A1 E1)."""
|
||||
def guitar(cls, tuning="standard"):
|
||||
"""Guitar with the given tuning.
|
||||
|
||||
Args:
|
||||
tuning: Tuning name or tuple of tone strings (high to low).
|
||||
Built-in tunings: standard, drop d, open g, open d,
|
||||
open e, open a, dadgad, half step down.
|
||||
"""
|
||||
from .tones import Tone
|
||||
return cls(tones=[
|
||||
Tone.from_string("G2", system="western"),
|
||||
Tone.from_string("D2", system="western"),
|
||||
Tone.from_string("A1", system="western"),
|
||||
Tone.from_string("E1", system="western"),
|
||||
])
|
||||
if isinstance(tuning, str):
|
||||
tuning = cls.TUNINGS[tuning]
|
||||
return cls(tones=[Tone.from_string(t, system="western") for t in tuning])
|
||||
|
||||
@classmethod
|
||||
def bass(cls, five_string=False):
|
||||
"""Standard bass guitar tuning.
|
||||
|
||||
Args:
|
||||
five_string: If True, adds a low B string (B0).
|
||||
"""
|
||||
from .tones import Tone
|
||||
strings = ["G2", "D2", "A1", "E1"]
|
||||
if five_string:
|
||||
strings.append("B0")
|
||||
return cls(tones=[Tone.from_string(t, system="western") for t in strings])
|
||||
|
||||
@classmethod
|
||||
def ukulele(cls):
|
||||
|
||||
+6
-1
@@ -1,4 +1,7 @@
|
||||
from ._statics import TEMPERAMENTS, TONES, DEGREES, SCALES, INDIAN_SCALES, SYSTEMS
|
||||
from ._statics import (
|
||||
TEMPERAMENTS, TONES, DEGREES, SCALES,
|
||||
INDIAN_SCALES, ARABIC_SCALES, JAPANESE_SCALES, SYSTEMS,
|
||||
)
|
||||
|
||||
|
||||
class System:
|
||||
@@ -129,4 +132,6 @@ class System:
|
||||
SYSTEMS = {
|
||||
"western": System(tone_names=TONES["western"], degrees=DEGREES["western"]),
|
||||
"indian": System(tone_names=TONES["indian"], degrees=DEGREES["indian"], scales=INDIAN_SCALES[12]),
|
||||
"arabic": System(tone_names=TONES["arabic"], degrees=DEGREES["arabic"], scales=ARABIC_SCALES[12]),
|
||||
"japanese": System(tone_names=TONES["japanese"], degrees=DEGREES["japanese"], scales=JAPANESE_SCALES[12]),
|
||||
}
|
||||
|
||||
@@ -1725,6 +1725,37 @@ def test_fretboard_ukulele_fingerings():
|
||||
assert len(fingering) == 4
|
||||
|
||||
|
||||
def test_fretboard_guitar_drop_d():
|
||||
fb = Fretboard.guitar("drop d")
|
||||
assert len(fb) == 6
|
||||
assert fb.tones[-1].name == "D"
|
||||
assert fb.tones[-1].octave == 2
|
||||
|
||||
|
||||
def test_fretboard_guitar_open_g():
|
||||
fb = Fretboard.guitar("open g")
|
||||
assert len(fb) == 6
|
||||
assert fb.tones[0].name == "D"
|
||||
|
||||
|
||||
def test_fretboard_guitar_custom_tuple():
|
||||
fb = Fretboard.guitar(("E4", "B3", "G3", "D3", "A2", "D2"))
|
||||
assert len(fb) == 6
|
||||
assert fb.tones[-1].name == "D"
|
||||
|
||||
|
||||
def test_fretboard_bass_five_string():
|
||||
fb = Fretboard.bass(five_string=True)
|
||||
assert len(fb) == 5
|
||||
assert fb.tones[-1].name == "B"
|
||||
|
||||
|
||||
def test_fretboard_tunings_dict():
|
||||
for name in Fretboard.TUNINGS:
|
||||
fb = Fretboard.guitar(name)
|
||||
assert len(fb) == 6, f"Tuning {name} should have 6 strings"
|
||||
|
||||
|
||||
# ── Ergonomic integration tests ─────────────────────────────────────────────
|
||||
|
||||
def test_ergonomic_workflow():
|
||||
@@ -1910,3 +1941,146 @@ def test_indian_scale_degree_access():
|
||||
assert bilawal[4].name == "Pa"
|
||||
assert bilawal["I"].name == "Sa"
|
||||
assert bilawal["V"].name == "Pa"
|
||||
|
||||
|
||||
# ── Arabic system ───────────────────────────────────────────────────────────
|
||||
|
||||
def test_arabic_system_exists():
|
||||
assert "arabic" in SYSTEMS
|
||||
assert SYSTEMS["arabic"].semitones == 12
|
||||
|
||||
|
||||
def test_arabic_tones():
|
||||
arabic = SYSTEMS["arabic"]
|
||||
names = [t.name for t in arabic.tones]
|
||||
assert "Do" in names
|
||||
assert "Re" in names
|
||||
assert "Sol" in names
|
||||
|
||||
|
||||
def test_arabic_do_pitch():
|
||||
"""Do4 should equal C4 = 261.63 Hz."""
|
||||
do = Tone.from_string("Do4", system="arabic")
|
||||
assert abs(do.frequency - 261.63) < 0.01
|
||||
|
||||
|
||||
def test_arabic_ajam_maqam():
|
||||
"""Ajam = major scale."""
|
||||
do = TonedScale(tonic="Do4", system=SYSTEMS["arabic"])
|
||||
ajam = do["ajam"]
|
||||
names = [t.name for t in ajam]
|
||||
assert names == ["Do", "Re", "Mi", "Fa", "Sol", "La", "Si", "Do"]
|
||||
|
||||
|
||||
def test_arabic_hijaz_maqam():
|
||||
"""Hijaz has augmented 2nd between 2nd and 3rd degrees."""
|
||||
do = TonedScale(tonic="Do4", system=SYSTEMS["arabic"])
|
||||
hijaz = do["hijaz"]
|
||||
names = [t.name for t in hijaz]
|
||||
assert names[0] == "Do"
|
||||
assert names[1] == "Reb" # flat 2nd
|
||||
assert names[2] == "Mi" # natural 3rd (augmented 2nd interval)
|
||||
|
||||
|
||||
def test_arabic_all_maqamat_available():
|
||||
do = TonedScale(tonic="Do4", system=SYSTEMS["arabic"])
|
||||
for maqam in ["ajam", "nahawand", "kurd", "hijaz", "nikriz",
|
||||
"bayati", "rast", "saba", "sikah", "jiharkah"]:
|
||||
assert maqam in do.scales, f"Missing maqam: {maqam}"
|
||||
|
||||
|
||||
def test_arabic_all_maqam_intervals_sum_to_12():
|
||||
arabic = SYSTEMS["arabic"]
|
||||
for name, scale in arabic.scales["maqam"].items():
|
||||
total = sum(scale["intervals"])
|
||||
assert total == 12, f"{name} intervals sum to {total}, not 12"
|
||||
|
||||
|
||||
def test_arabic_ajam_equals_western_major():
|
||||
arabic = SYSTEMS["arabic"]
|
||||
western = SYSTEMS["western"]
|
||||
ajam = arabic.scales["maqam"]["ajam"]["intervals"]
|
||||
major = western.scales["heptatonic"]["major"]["intervals"]
|
||||
assert ajam == major
|
||||
|
||||
|
||||
def test_arabic_tone_arithmetic():
|
||||
do = Tone.from_string("Do4", system="arabic")
|
||||
assert (do + 2).name == "Re"
|
||||
assert (do + 4).name == "Mi"
|
||||
assert (do + 7).name == "Sol"
|
||||
|
||||
|
||||
# ── Japanese system ─────────────────────────────────────────────────────────
|
||||
|
||||
def test_japanese_system_exists():
|
||||
assert "japanese" in SYSTEMS
|
||||
assert SYSTEMS["japanese"].semitones == 12
|
||||
|
||||
|
||||
def test_japanese_hirajoshi():
|
||||
"""Hirajoshi: C D Eb G Ab."""
|
||||
c = TonedScale(tonic="C4", system=SYSTEMS["japanese"])
|
||||
h = c["hirajoshi"]
|
||||
names = [t.name for t in h]
|
||||
assert names == ["C", "D", "D#", "G", "G#", "C"]
|
||||
|
||||
|
||||
def test_japanese_in_scale():
|
||||
"""In (Miyako-bushi): C Db F G Ab."""
|
||||
c = TonedScale(tonic="C4", system=SYSTEMS["japanese"])
|
||||
s = c["in"]
|
||||
names = [t.name for t in s]
|
||||
assert names == ["C", "C#", "F", "G", "G#", "C"]
|
||||
|
||||
|
||||
def test_japanese_yo_scale():
|
||||
"""Yo: C D F G Bb."""
|
||||
c = TonedScale(tonic="C4", system=SYSTEMS["japanese"])
|
||||
s = c["yo"]
|
||||
names = [t.name for t in s]
|
||||
assert names == ["C", "D", "F", "G", "A#", "C"]
|
||||
|
||||
|
||||
def test_japanese_iwato():
|
||||
"""Iwato: C Db F Gb Bb."""
|
||||
c = TonedScale(tonic="C4", system=SYSTEMS["japanese"])
|
||||
s = c["iwato"]
|
||||
names = [t.name for t in s]
|
||||
assert names == ["C", "C#", "F", "F#", "A#", "C"]
|
||||
|
||||
|
||||
def test_japanese_kumoi():
|
||||
"""Kumoi: C D Eb G A."""
|
||||
c = TonedScale(tonic="C4", system=SYSTEMS["japanese"])
|
||||
s = c["kumoi"]
|
||||
names = [t.name for t in s]
|
||||
assert names == ["C", "D", "D#", "G", "A", "C"]
|
||||
|
||||
|
||||
def test_japanese_ritsu():
|
||||
"""Ritsu (gagaku): C D Eb F G A Bb = Dorian."""
|
||||
c = TonedScale(tonic="C4", system=SYSTEMS["japanese"])
|
||||
s = c["ritsu"]
|
||||
names = [t.name for t in s]
|
||||
assert names == ["C", "D", "D#", "F", "G", "A", "A#", "C"]
|
||||
|
||||
|
||||
def test_japanese_all_scales_available():
|
||||
c = TonedScale(tonic="C4", system=SYSTEMS["japanese"])
|
||||
for scale in ["hirajoshi", "in", "yo", "iwato", "kumoi", "insen", "ritsu", "ryo"]:
|
||||
assert scale in c.scales, f"Missing scale: {scale}"
|
||||
|
||||
|
||||
def test_japanese_pentatonic_intervals_sum_to_12():
|
||||
japanese = SYSTEMS["japanese"]
|
||||
for name, scale in japanese.scales["pentatonic"].items():
|
||||
total = sum(scale["intervals"])
|
||||
assert total == 12, f"{name} intervals sum to {total}, not 12"
|
||||
|
||||
|
||||
def test_japanese_heptatonic_intervals_sum_to_12():
|
||||
japanese = SYSTEMS["japanese"]
|
||||
for name, scale in japanese.scales["heptatonic"].items():
|
||||
total = sum(scale["intervals"])
|
||||
assert total == 12, f"{name} intervals sum to {total}, not 12"
|
||||
|
||||
Reference in New Issue
Block a user