mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 23:00:20 +00:00
Add mandolin, mandola, violin, viola, cello, banjo, 12-string presets
New Fretboard presets: - Fretboard.mandolin() — E5 A4 D4 G3 (tuned in 5ths) - Fretboard.mandola() — A4 D4 G3 C3 (octave below mandolin) - Fretboard.violin() — E5 A4 D4 G3 (same as mandolin) - Fretboard.viola() — A4 D4 G3 C3 (5th below violin) - Fretboard.cello() — A3 D3 G2 C2 (octave below viola) - Fretboard.banjo(tuning) — open G, open D, double C - Fretboard.twelve_string() — 12-string guitar (6 doubled courses) Updated fretboard docs with string family section and custom instrument examples (mandola, baritone ukulele, upright bass). 347 tests passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+57
-13
@@ -31,10 +31,21 @@ Preset Tunings
|
||||
|
||||
from pytheory import Fretboard
|
||||
|
||||
# Guitars
|
||||
guitar = Fretboard.guitar() # Standard EADGBE
|
||||
twelve = Fretboard.twelve_string() # 12-string (6 doubled courses)
|
||||
bass = Fretboard.bass() # Standard EADG
|
||||
bass5 = Fretboard.bass(five_string=True) # 5-string BEADG
|
||||
ukulele = Fretboard.ukulele() # GCEA (re-entrant)
|
||||
|
||||
# Plucked strings
|
||||
ukulele = Fretboard.ukulele() # GCEA (re-entrant)
|
||||
mandolin = Fretboard.mandolin() # GDAE (tuned in 5ths)
|
||||
banjo = Fretboard.banjo() # Open G (5-string with drone)
|
||||
|
||||
# Bowed strings
|
||||
violin = Fretboard.violin() # GDAE
|
||||
viola = Fretboard.viola() # CGDA (5th below violin)
|
||||
cello = Fretboard.cello() # CGDA (octave below viola)
|
||||
|
||||
Alternate Guitar Tunings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -58,31 +69,64 @@ PyTheory supports several common alternate tunings, including
|
||||
# Custom tuning with any notes
|
||||
custom = Fretboard.guitar(("D4", "A3", "F#3", "D3", "A2", "D2"))
|
||||
|
||||
The String Family
|
||||
-----------------
|
||||
|
||||
All four members of the orchestral string family are tuned in
|
||||
`perfect fifths <https://en.wikipedia.org/wiki/Perfect_fifth>`_
|
||||
(7 semitones between adjacent strings):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
violin = Fretboard.violin() # E5 A4 D4 G3 — soprano
|
||||
viola = Fretboard.viola() # A4 D4 G3 C3 — alto (5th below violin)
|
||||
cello = Fretboard.cello() # A3 D3 G2 C2 — tenor/bass (octave below viola)
|
||||
|
||||
Unlike fretted instruments, bowed strings have no frets — the player
|
||||
can produce any pitch along the fingerboard, enabling vibrato and
|
||||
microtonal inflections.
|
||||
|
||||
Other String Instruments
|
||||
------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
mandolin = Fretboard.mandolin() # E5 A4 D4 G3 (violin tuning)
|
||||
banjo = Fretboard.banjo() # Open G (with high drone string)
|
||||
banjo_d = Fretboard.banjo("open d") # Open D (clawhammer)
|
||||
twelve = Fretboard.twelve_string() # 12 strings (6 doubled courses)
|
||||
|
||||
Custom Instruments
|
||||
------------------
|
||||
|
||||
Any fretted instrument can be modeled, including `banjo <https://en.wikipedia.org/wiki/Banjo>`_,
|
||||
`mandolin <https://en.wikipedia.org/wiki/Mandolin>`_, and more:
|
||||
Any stringed instrument can be modeled:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pytheory import Tone, Fretboard
|
||||
|
||||
# Banjo (open G tuning)
|
||||
banjo = Fretboard(tones=[
|
||||
Tone.from_string("D4"),
|
||||
# Mandola (octave below mandolin, like viola to violin)
|
||||
mandola = Fretboard(tones=[
|
||||
Tone.from_string("E4"),
|
||||
Tone.from_string("A3"),
|
||||
Tone.from_string("D3"),
|
||||
Tone.from_string("G2"),
|
||||
])
|
||||
|
||||
# Baritone ukulele (DGBE — like the top 4 guitar strings)
|
||||
bari_uke = Fretboard(tones=[
|
||||
Tone.from_string("E4"),
|
||||
Tone.from_string("B3"),
|
||||
Tone.from_string("G3"),
|
||||
Tone.from_string("D3"),
|
||||
Tone.from_string("G4"), # 5th string (high drone)
|
||||
])
|
||||
|
||||
# Mandolin
|
||||
mandolin = Fretboard(tones=[
|
||||
Tone.from_string("E5"),
|
||||
Tone.from_string("A4"),
|
||||
Tone.from_string("D4"),
|
||||
Tone.from_string("G3"),
|
||||
# Double bass / upright bass
|
||||
upright = Fretboard(tones=[
|
||||
Tone.from_string("G2"),
|
||||
Tone.from_string("D2"),
|
||||
Tone.from_string("A1"),
|
||||
Tone.from_string("E1"),
|
||||
])
|
||||
|
||||
Getting Fingerings
|
||||
|
||||
+123
-1
@@ -589,7 +589,10 @@ class Fretboard:
|
||||
|
||||
@classmethod
|
||||
def ukulele(cls):
|
||||
"""Standard ukulele tuning (A4 E4 C4 G4)."""
|
||||
"""Standard ukulele tuning (A4 E4 C4 G4).
|
||||
|
||||
Re-entrant tuning: the G4 string is higher than C4.
|
||||
"""
|
||||
from .tones import Tone
|
||||
return cls(tones=[
|
||||
Tone.from_string("A4", system="western"),
|
||||
@@ -598,6 +601,125 @@ class Fretboard:
|
||||
Tone.from_string("G4", system="western"),
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def mandolin(cls):
|
||||
"""Standard mandolin tuning (E5 A4 D4 G3).
|
||||
|
||||
Tuned in fifths, same as a violin but one octave relationship.
|
||||
Strings are typically doubled (paired courses).
|
||||
"""
|
||||
from .tones import Tone
|
||||
return cls(tones=[
|
||||
Tone.from_string("E5", system="western"),
|
||||
Tone.from_string("A4", system="western"),
|
||||
Tone.from_string("D4", system="western"),
|
||||
Tone.from_string("G3", system="western"),
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def mandola(cls):
|
||||
"""Standard mandola tuning (A4 D4 G3 C3).
|
||||
|
||||
An octave below the mandolin, same relationship as viola to
|
||||
violin. Tuned in fifths: C3-G3-D4-A4.
|
||||
"""
|
||||
from .tones import Tone
|
||||
return cls(tones=[
|
||||
Tone.from_string("A4", system="western"),
|
||||
Tone.from_string("D4", system="western"),
|
||||
Tone.from_string("G3", system="western"),
|
||||
Tone.from_string("C3", system="western"),
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def violin(cls):
|
||||
"""Standard violin tuning (E5 A4 D4 G3).
|
||||
|
||||
Tuned in perfect fifths. The violin has no frets — intonation
|
||||
is continuous, allowing vibrato and microtonal inflections
|
||||
not possible on fretted instruments.
|
||||
"""
|
||||
from .tones import Tone
|
||||
return cls(tones=[
|
||||
Tone.from_string("E5", system="western"),
|
||||
Tone.from_string("A4", system="western"),
|
||||
Tone.from_string("D4", system="western"),
|
||||
Tone.from_string("G3", system="western"),
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def viola(cls):
|
||||
"""Standard viola tuning (A4 D4 G3 C3).
|
||||
|
||||
A perfect fifth below the violin. The viola's darker, warmer
|
||||
tone comes from its larger body and lower register.
|
||||
"""
|
||||
from .tones import Tone
|
||||
return cls(tones=[
|
||||
Tone.from_string("A4", system="western"),
|
||||
Tone.from_string("D4", system="western"),
|
||||
Tone.from_string("G3", system="western"),
|
||||
Tone.from_string("C3", system="western"),
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def cello(cls):
|
||||
"""Standard cello tuning (A3 D3 G2 C2).
|
||||
|
||||
An octave below the viola. Tuned in fifths. The cello spans
|
||||
the range of the human voice — tenor through bass.
|
||||
"""
|
||||
from .tones import Tone
|
||||
return cls(tones=[
|
||||
Tone.from_string("A3", system="western"),
|
||||
Tone.from_string("D3", system="western"),
|
||||
Tone.from_string("G2", system="western"),
|
||||
Tone.from_string("C2", system="western"),
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def banjo(cls, tuning="open g"):
|
||||
"""Banjo with the given tuning.
|
||||
|
||||
Args:
|
||||
tuning: ``"open g"`` (default, bluegrass) or ``"open d"``
|
||||
(old-time, clawhammer). The 5th string is a high
|
||||
drone — a defining feature of the banjo sound.
|
||||
|
||||
Standard open G: G4 D3 G3 B3 D4 (5th string is the short
|
||||
high G4 drone).
|
||||
"""
|
||||
from .tones import Tone
|
||||
tunings = {
|
||||
"open g": ("D4", "B3", "G3", "D3", "G4"),
|
||||
"open d": ("D4", "A3", "F#3", "D3", "A4"),
|
||||
"double c": ("D4", "C4", "G3", "C3", "G4"),
|
||||
}
|
||||
if isinstance(tuning, str):
|
||||
tuning = tunings[tuning]
|
||||
return cls(tones=[Tone.from_string(t, system="western") for t in tuning])
|
||||
|
||||
@classmethod
|
||||
def twelve_string(cls):
|
||||
"""12-string guitar in standard tuning.
|
||||
|
||||
The lower 4 courses are doubled at the octave; the upper 2
|
||||
are doubled in unison. This creates the characteristic
|
||||
shimmering, chorus-like sound.
|
||||
|
||||
Represented as 12 strings (high to low, pairs together).
|
||||
"""
|
||||
from .tones import Tone
|
||||
strings = [
|
||||
"E4", "E4", # 1st course (unison)
|
||||
"B3", "B3", # 2nd course (unison)
|
||||
"G4", "G3", # 3rd course (octave)
|
||||
"D4", "D3", # 4th course (octave)
|
||||
"A3", "A2", # 5th course (octave)
|
||||
"E3", "E2", # 6th course (octave)
|
||||
]
|
||||
return cls(tones=[Tone.from_string(t, system="western") for t in strings])
|
||||
|
||||
def fingering(self, *positions):
|
||||
if not len(positions) == len(self.tones):
|
||||
raise ValueError(
|
||||
|
||||
@@ -1829,6 +1829,59 @@ def test_fretboard_tunings_dict():
|
||||
assert len(fb) == 6, f"Tuning {name} should have 6 strings"
|
||||
|
||||
|
||||
def test_fretboard_mandolin():
|
||||
fb = Fretboard.mandolin()
|
||||
assert len(fb) == 4
|
||||
assert fb.tones[0].name == "E"
|
||||
assert fb.tones[-1].name == "G"
|
||||
|
||||
|
||||
def test_fretboard_violin():
|
||||
fb = Fretboard.violin()
|
||||
assert len(fb) == 4
|
||||
names = [t.name for t in fb]
|
||||
assert names == ["E", "A", "D", "G"]
|
||||
|
||||
|
||||
def test_fretboard_viola():
|
||||
fb = Fretboard.viola()
|
||||
assert len(fb) == 4
|
||||
names = [t.name for t in fb]
|
||||
assert names == ["A", "D", "G", "C"]
|
||||
|
||||
|
||||
def test_fretboard_cello():
|
||||
fb = Fretboard.cello()
|
||||
assert len(fb) == 4
|
||||
names = [t.name for t in fb]
|
||||
assert names == ["A", "D", "G", "C"]
|
||||
assert fb.tones[0].octave == 3
|
||||
|
||||
|
||||
def test_fretboard_banjo():
|
||||
fb = Fretboard.banjo()
|
||||
assert len(fb) == 5
|
||||
assert fb.tones[-1].name == "G" # high drone string
|
||||
|
||||
|
||||
def test_fretboard_banjo_open_d():
|
||||
fb = Fretboard.banjo("open d")
|
||||
assert len(fb) == 5
|
||||
|
||||
|
||||
def test_fretboard_twelve_string():
|
||||
fb = Fretboard.twelve_string()
|
||||
assert len(fb) == 12
|
||||
|
||||
|
||||
def test_fretboard_violin_tuned_in_fifths():
|
||||
"""Violin strings should be a perfect 5th apart."""
|
||||
fb = Fretboard.violin()
|
||||
for i in range(len(fb.tones) - 1):
|
||||
interval = fb.tones[i] - fb.tones[i + 1]
|
||||
assert interval == 7, f"Strings {i} and {i+1} not a 5th apart"
|
||||
|
||||
|
||||
# ── Ergonomic integration tests ─────────────────────────────────────────────
|
||||
|
||||
def test_ergonomic_workflow():
|
||||
|
||||
Reference in New Issue
Block a user