from typing import Any, Optional
from musicscore.exceptions import TupletNormalTypeError
from musicxml import XMLTimeModification, XMLTuplet
TUPLETACTUALTONORMALNOTES = {
3: 2,
5: 4,
6: 4,
7: 4,
9: 8,
10: 8,
11: 8,
12: 8,
13: 8,
14: 8,
15: 8,
}
TUPLETNORMALTYPES = {8: "32nd", 4: "16th", 2: "eighth", 1: "quarter", 0.5: "half"}
[docs]class Tuplet:
def __init__(
self,
actual_notes=3,
normal_notes=None,
normal_type=None,
bracket_type=None,
bracket_number=None,
quarter_duration=1,
):
self._actual_notes = None
self._normal_notes = None
self._normal_type = None
self._bracket_type = None
self._bracket_number = None
self._quarter_duration = None
self.actual_notes = actual_notes
self.normal_notes = normal_notes
self.normal_type = normal_type
self.bracket_type = bracket_type
self.bracket_number = bracket_number
self.quarter_duration = quarter_duration
@property
def actual_notes(self) -> int:
return self._actual_notes
@actual_notes.setter
def actual_notes(self, val):
if not isinstance(val, int):
raise TypeError
if val < 2:
raise ValueError
self._actual_notes = val
@property
def normal_notes(self) -> int:
if self._normal_notes is None:
return TUPLETACTUALTONORMALNOTES.get(self.actual_notes)
return self._normal_notes
@normal_notes.setter
def normal_notes(self, val):
if val is not None:
if not isinstance(val, int):
raise TypeError
if val < 2:
raise ValueError
self._normal_notes = val
@property
def normal_type(self) -> str:
if self._normal_type is None:
try:
return TUPLETNORMALTYPES[self.normal_notes / self.quarter_duration]
except KeyError:
raise TupletNormalTypeError(
f"Cannot find normal type of normal_notes {self.normal_notes} with quarter_duration {self.quarter_duration}. You can set normal_type manually."
)
return self._normal_type
@normal_type.setter
def normal_type(self, val):
permitted = [
None,
"1024th",
"512th",
"256th",
"128th",
"64th",
"32nd",
"16th",
"eighth",
"quarter",
"half",
"whole",
"breve",
"long",
"maxima",
]
if val not in permitted:
raise ValueError(f"Permitted type values are: {permitted}")
self._normal_type = val
@property
def bracket_type(self) -> Optional[str]:
return self._bracket_type
@bracket_type.setter
def bracket_type(self, val):
permitted = [None, "start", "stop"]
if val not in permitted:
raise ValueError(f"Permitted bracket_type values are: {permitted}")
self._bracket_type = val
@property
def bracket_number(self) -> int:
if self._bracket_number is None and self.bracket_type:
return 1
return self._bracket_number
@bracket_number.setter
def bracket_number(self, val):
if val is not None:
if not isinstance(val, int):
raise TypeError
if val < 1:
raise ValueError
self._bracket_number = val
@property
def ratio(self):
return (self.actual_notes, self.normal_notes)
[docs] def get_xml_time_modification(self) -> "XMLTimeModification":
output = XMLTimeModification()
output.xml_actual_notes = self.actual_notes
output.xml_normal_notes = self.normal_notes
output.xml_normal_type = self.normal_type
return output
[docs] def get_xml_tuplet(self) -> XMLTuplet:
if self.bracket_type:
output = XMLTuplet(type=self.bracket_type, number=self.bracket_number)
if self.bracket_type == "start":
output.bracket = "yes"
return output
[docs]class SimplifiedSextuplets:
_ATTRIBUTES = {"simplified_sextuplets"}
def __init__(
self, simplified_sextuplets: Optional[bool] = None, *args: Any, **kwargs: Any
) -> None:
super().__init__(*args, **kwargs)
self._simplified_sextuplets = None
self.simplified_sextuplets = simplified_sextuplets
@property
def simplified_sextuplets(self) -> bool:
if self._simplified_sextuplets is None:
if self.up:
return self.up.simplified_sextuplets
else:
return False
return self._simplified_sextuplets
@simplified_sextuplets.setter
def simplified_sextuplets(self, value: bool) -> None:
self._simplified_sextuplets = value