Add 9 world instruments: bouzouki, oud, sitar, shamisen, erhu, charango, pipa, balalaika, lute

World instruments:
- bouzouki (Irish GDAD / Greek CFAD)
- oud (Arabic, 6 courses, fretless)
- sitar (Indian, 7 main strings)
- shamisen (Japanese, 3 strings, honchoshi tuning)
- erhu (Chinese, 2 strings, bowed)
- charango (Andean, 5 courses, re-entrant)
- pipa (Chinese lute, 4 strings)
- balalaika (Russian, 3 strings, 2 in unison)
- lute (Renaissance, 6 courses)

24 instrument presets total. 365 tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-22 06:52:39 -04:00
parent 8bf716d1db
commit 5e09e64c2d
2 changed files with 201 additions and 0 deletions
+146
View File
@@ -794,6 +794,152 @@ class Fretboard:
"F#3", "E3", "D3", "B2"]
return cls(tones=[Tone.from_string(s, system="western") for s in strings])
@classmethod
def bouzouki(cls, variant="irish"):
"""Bouzouki tuning.
Args:
variant: ``"irish"`` (default, GDAD) or ``"greek"`` (CFAD).
The Irish bouzouki is a staple of Celtic music, usually tuned
in unison or octave pairs. The Greek bouzouki traditionally
has 3 or 4 courses and a brighter, more metallic sound.
"""
from .tones import Tone
tunings = {
"irish": ("D4", "A3", "D3", "G2"),
"greek": ("D4", "A3", "F3", "C3"),
}
if isinstance(variant, str):
variant = tunings[variant]
return cls(tones=[Tone.from_string(t, system="western") for t in variant])
@classmethod
def oud(cls):
"""Standard Arabic oud tuning (C4 G3 D3 A2 G2 C2).
The oud is the ancestor of the European lute and the defining
instrument of Arabic, Turkish, and Persian classical music.
It is fretless, allowing the quarter-tone inflections
essential to maqam performance. 6 courses (11 strings),
typically tuned in fourths.
"""
from .tones import Tone
strings = ["C4", "G3", "D3", "A2", "G2", "C2"]
return cls(tones=[Tone.from_string(t, system="western") for t in strings])
@classmethod
def sitar(cls):
"""Sitar main playing strings (approximation).
The sitar typically has 6-7 main strings and 11-13 sympathetic
strings (taraf). This models the main playing strings in a
common tuning. The actual tuning varies by raga and tradition.
Main strings: Sa Sa Pa Sa Re Sa Ma (approximated in 12-TET).
Represented here as the most common Ravi Shankar school tuning.
"""
from .tones import Tone
# Common Ravi Shankar tuning mapped to Western notes
# (sitar is tuned relative to Sa, typically C# or D)
strings = ["C4", "C3", "G3", "C3", "D3", "C2", "F2"]
return cls(tones=[Tone.from_string(t, system="western") for t in strings])
@classmethod
def shamisen(cls):
"""Standard shamisen tuning — honchoshi (C4 G3 C3).
The shamisen is a 3-stringed Japanese instrument played with
a large plectrum (bachi). Three standard tunings:
- honchoshi (本調子): root-5th-root
- niagari (二上り): root-5th-2nd (raises 2nd string)
- sansagari (三下り): root-5th-b7th (lowers 3rd string)
"""
from .tones import Tone
return cls(tones=[
Tone.from_string("C4", system="western"),
Tone.from_string("G3", system="western"),
Tone.from_string("C3", system="western"),
])
@classmethod
def erhu(cls):
"""Standard erhu tuning (A4 D4).
The erhu is a 2-stringed Chinese bowed instrument with a
hauntingly vocal quality. Tuned a fifth apart. No fingerboard
— the player presses the strings without touching the neck,
allowing continuous pitch bending.
"""
from .tones import Tone
return cls(tones=[
Tone.from_string("A4", system="western"),
Tone.from_string("D4", system="western"),
])
@classmethod
def charango(cls):
"""Standard charango tuning (E5 A4 E5 C5 G4).
A small Andean stringed instrument, traditionally made from
an armadillo shell. 5 doubled courses with re-entrant tuning
— the 3rd course (E5) is the highest pitched, creating the
charango's bright, sparkling sound.
"""
from .tones import Tone
return cls(tones=[
Tone.from_string("E5", system="western"),
Tone.from_string("A4", system="western"),
Tone.from_string("E5", system="western"),
Tone.from_string("C5", system="western"),
Tone.from_string("G4", system="western"),
])
@classmethod
def pipa(cls):
"""Standard pipa tuning (D4 A3 E3 A2).
The pipa is a 4-stringed Chinese lute with a pear-shaped
body, dating back over 2000 years. Known for its percussive
attack and rapid tremolo technique.
"""
from .tones import Tone
return cls(tones=[
Tone.from_string("D4", system="western"),
Tone.from_string("A3", system="western"),
Tone.from_string("E3", system="western"),
Tone.from_string("A2", system="western"),
])
@classmethod
def balalaika(cls):
"""Standard balalaika prima tuning (A4 E4 E4).
The Russian balalaika has a distinctive triangular body and
3 strings. The two lower strings are tuned in unison — a
unique feature that gives it a natural chorus effect.
"""
from .tones import Tone
return cls(tones=[
Tone.from_string("A4", system="western"),
Tone.from_string("E4", system="western"),
Tone.from_string("E4", system="western"),
])
@classmethod
def lute(cls):
"""Renaissance lute in G tuning (6 courses).
The European lute was the dominant instrument of the
Renaissance (15th-17th century). Tuned in fourths with
a major third between the 3rd and 4th courses — the
same intervallic pattern as a modern guitar.
"""
from .tones import Tone
strings = ["G4", "D4", "A3", "F3", "C3", "G2"]
return cls(tones=[Tone.from_string(t, system="western") for t in strings])
@classmethod
def twelve_string(cls):
"""12-string guitar in standard tuning.
+55
View File
@@ -1941,12 +1941,67 @@ def test_all_instruments_create():
"mandolin", "mandola", "octave_mandolin", "mandocello",
"violin", "viola", "cello", "double_bass",
"banjo", "harp", "pedal_steel",
"bouzouki", "oud", "sitar", "shamisen", "erhu",
"charango", "pipa", "balalaika", "lute",
]
for name in instruments:
fb = getattr(Fretboard, name)()
assert len(fb) > 0, f"{name} has no strings"
def test_fretboard_oud():
fb = Fretboard.oud()
assert len(fb) == 6
def test_fretboard_shamisen():
fb = Fretboard.shamisen()
assert len(fb) == 3
def test_fretboard_erhu():
fb = Fretboard.erhu()
assert len(fb) == 2
assert fb.tones[0] - fb.tones[1] == 7 # tuned in 5ths
def test_fretboard_bouzouki_irish():
fb = Fretboard.bouzouki("irish")
assert len(fb) == 4
def test_fretboard_bouzouki_greek():
fb = Fretboard.bouzouki("greek")
assert len(fb) == 4
def test_fretboard_charango():
fb = Fretboard.charango()
assert len(fb) == 5
def test_fretboard_balalaika():
fb = Fretboard.balalaika()
assert len(fb) == 3
# Two unison strings
assert fb.tones[1].name == fb.tones[2].name
def test_fretboard_lute():
fb = Fretboard.lute()
assert len(fb) == 6
def test_fretboard_sitar():
fb = Fretboard.sitar()
assert len(fb) == 7
def test_fretboard_pipa():
fb = Fretboard.pipa()
assert len(fb) == 4
# ── Ergonomic integration tests ─────────────────────────────────────────────
def test_ergonomic_workflow():