Changed default options to immutable MappingProxyType
Options as immutable dicts are less prone to bugs
This commit is contained in:
@ -319,18 +319,19 @@ class Sequence(Meta):
|
||||
if isinstance(item, ListOperation):
|
||||
yield from item.evaluate(options)
|
||||
elif isinstance(item, RepeatedSequence):
|
||||
item.evaluate_values(options)
|
||||
repeats = item.repeats.get_value(options)
|
||||
yield from _normal_repeat(item.evaluated_values, repeats, options)
|
||||
elif isinstance(item, RepeatedListSequence):
|
||||
repeats = item.repeats.get_value(options)
|
||||
yield from _generative_repeat(item, repeats, options)
|
||||
elif isinstance(item, Subdivision):
|
||||
item.evaluated_values = list(item.evaluate_tree(options))
|
||||
item.evaluate_values(options)
|
||||
yield item
|
||||
else:
|
||||
yield from item.evaluate_tree(options)
|
||||
elif isinstance(item, Range):
|
||||
yield from item.evaluate(options)
|
||||
yield from item.evaluate(options)
|
||||
elif isinstance(item, Cyclic):
|
||||
yield from _resolve_item(item.get_value(), options)
|
||||
elif isinstance(item, Euclid):
|
||||
@ -486,7 +487,7 @@ class Sequence(Meta):
|
||||
class Ziffers(Sequence):
|
||||
"""Main class for holding options and the current state"""
|
||||
|
||||
options: dict = field(default_factory=DEFAULT_OPTIONS)
|
||||
options: dict = field(default_factory=DEFAULT_OPTIONS.copy())
|
||||
start_options: dict = None
|
||||
loop_i: int = field(default=0, init=False)
|
||||
cycle_i: int = field(default=0, init=False)
|
||||
@ -514,11 +515,11 @@ class Ziffers(Sequence):
|
||||
# pylint: disable=locally-disabled, dangerous-default-value
|
||||
def init_opts(self, options=None):
|
||||
"""Evaluate the Ziffers tree using the options"""
|
||||
self.options.update(DEFAULT_OPTIONS)
|
||||
self.options.update(DEFAULT_OPTIONS.copy())
|
||||
if options:
|
||||
self.options.update(options)
|
||||
else:
|
||||
self.options = DEFAULT_OPTIONS
|
||||
self.options = DEFAULT_OPTIONS.copy()
|
||||
|
||||
self.start_options = self.options.copy()
|
||||
self.init_tree(self.options)
|
||||
@ -531,12 +532,14 @@ class Ziffers(Sequence):
|
||||
self.init_tree(self.options)
|
||||
|
||||
def init_tree(self, options):
|
||||
"""Initialize evaluated values and perform post-evaluation"""
|
||||
self.evaluated_values = list(self.evaluate_tree(options))
|
||||
self.evaluated_values = list(self.post_check())
|
||||
self.evaluated_values = list(self.post_evaluation())
|
||||
self.iterator = iter(self.evaluated_values)
|
||||
self.cycle_length = len(self.evaluated_values)
|
||||
|
||||
def post_check(self):
|
||||
def post_evaluation(self):
|
||||
"""Post-evaluation performs evaluation that can only be done after initial evaluation"""
|
||||
for item in self.evaluated_values:
|
||||
if isinstance(item, Subdivision):
|
||||
yield from item.evaluate_durations()
|
||||
@ -568,7 +571,7 @@ class Ziffers(Sequence):
|
||||
Args:
|
||||
options (dict): Options as a dict
|
||||
"""
|
||||
self.options = DEFAULT_OPTIONS | options
|
||||
self.options = DEFAULT_OPTIONS.copy() | options
|
||||
|
||||
def pitch_classes(self) -> list[int]:
|
||||
"""Return list of pitch classes as ints"""
|
||||
@ -650,6 +653,10 @@ class RepeatedListSequence(Sequence):
|
||||
class Subdivision(Sequence):
|
||||
"""Class for subdivisions"""
|
||||
|
||||
def evaluate_values(self, options):
|
||||
"""Evaluate values and store to evaluated_values"""
|
||||
self.evaluated_values = list(self.evaluate_tree(options))
|
||||
|
||||
def evaluate_durations(self, duration=None):
|
||||
"""Calculate new durations by dividing with the number of items in the sequence"""
|
||||
if duration is None:
|
||||
@ -702,17 +709,17 @@ class Range(Item):
|
||||
end: int = field(default=None)
|
||||
|
||||
def evaluate(self, options):
|
||||
if self.start<self.end:
|
||||
for i in range(self.start,self.end+1):
|
||||
"""Evaluates range and generates a generator of Pitches"""
|
||||
if self.start < self.end:
|
||||
for i in range(self.start, self.end + 1):
|
||||
yield Pitch(pitch_class=i, kwargs=options)
|
||||
elif self.start>self.end:
|
||||
for i in reversed(range(self.end,self.start+1)):
|
||||
elif self.start > self.end:
|
||||
for i in reversed(range(self.end, self.start + 1)):
|
||||
yield Pitch(pitch_class=i, kwargs=options)
|
||||
else:
|
||||
yield Pitch(pitch_class=self.start, kwargs=options)
|
||||
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class Operator(Item):
|
||||
"""Class for math operators"""
|
||||
@ -730,8 +737,7 @@ class ListOperation(Sequence):
|
||||
super().__post_init__()
|
||||
self.evaluated_values = self.evaluate()
|
||||
|
||||
# pylint: disable=locally-disabled, dangerous-default-value
|
||||
def evaluate(self, options=DEFAULT_OPTIONS):
|
||||
def evaluate(self, options=DEFAULT_OPTIONS.copy()):
|
||||
"""Evaluates the operation"""
|
||||
|
||||
def filter_operation(input_list):
|
||||
@ -766,7 +772,7 @@ class ListOperation(Sequence):
|
||||
(right.values if isinstance(right, Sequence) else [right]), left
|
||||
)
|
||||
left = [
|
||||
#TODO: Get options from x value?
|
||||
# TODO: Get options from x value?
|
||||
Pitch(
|
||||
pitch_class=operation(x.get_value(options), y.get_value(options)),
|
||||
kwargs=options,
|
||||
@ -858,12 +864,13 @@ class RepeatedSequence(Sequence):
|
||||
|
||||
evaluated_values: list = None
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
self.evaluated_values = list(self.evaluate())
|
||||
def evaluate_values(self, options):
|
||||
"""Evaluate values and store to evaluated_values"""
|
||||
self.evaluated_values = list(self.evaluate(options))
|
||||
|
||||
def evaluate(self):
|
||||
def evaluate(self, options: dict):
|
||||
"""Evaluate repeated sequence partially. Leaves Cycles intact."""
|
||||
self.local_options = options.copy()
|
||||
for item in self.values:
|
||||
if isinstance(item, Sequence):
|
||||
if isinstance(item, ListOperation):
|
||||
@ -879,6 +886,9 @@ class RepeatedSequence(Sequence):
|
||||
elif isinstance(item, Rest):
|
||||
yield item.get_updated_item(self.local_options)
|
||||
elif isinstance(item, Range):
|
||||
yield from item.evaluate(options)
|
||||
yield from item.evaluate(self.local_options)
|
||||
elif isinstance(item, (Event, RandomInteger)):
|
||||
yield Pitch(pitch_class=item.get_value(self.local_options), kwargs=self.local_options)
|
||||
yield Pitch(
|
||||
pitch_class=item.get_value(self.local_options),
|
||||
kwargs=self.local_options,
|
||||
)
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
""" Default options for Ziffers """
|
||||
import operator
|
||||
from types import MappingProxyType
|
||||
|
||||
DEFAULT_DURS = {
|
||||
DEFAULT_DURS = MappingProxyType({
|
||||
"m": 8.0, # 15360/1920
|
||||
"k": 10240 / 1920, # ~5.333
|
||||
"l": 4.0, # 7680/1920
|
||||
@ -37,13 +38,18 @@ DEFAULT_DURS = {
|
||||
"j": 15 / 1920, # ~0.0078 - 1/128
|
||||
"o": 8 / 1920, # ~0.00416
|
||||
"z": 0.0, # 0
|
||||
}
|
||||
})
|
||||
|
||||
DEFAULT_OCTAVE = 4
|
||||
|
||||
DEFAULT_OPTIONS = {"octave": 0, "duration": 0.25, "key": "C4", "scale": "IONIAN"}
|
||||
DEFAULT_OPTIONS = MappingProxyType({
|
||||
"octave": 0,
|
||||
"duration": 0.25,
|
||||
"key": "C4",
|
||||
"scale": "IONIAN"
|
||||
})
|
||||
|
||||
OPERATORS = {
|
||||
OPERATORS = MappingProxyType({
|
||||
"+": operator.add,
|
||||
"-": operator.sub,
|
||||
"*": operator.mul,
|
||||
@ -53,10 +59,10 @@ OPERATORS = {
|
||||
"&": operator.and_,
|
||||
"<<": operator.ilshift,
|
||||
">>": operator.irshift
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
NOTES_TO_INTERVALS = {
|
||||
NOTES_TO_INTERVALS = MappingProxyType({
|
||||
"C": 0,
|
||||
"Cs": 1,
|
||||
"D": 2,
|
||||
@ -69,9 +75,9 @@ NOTES_TO_INTERVALS = {
|
||||
"A": 9,
|
||||
"Bb": 10,
|
||||
"B": 11,
|
||||
}
|
||||
})
|
||||
|
||||
INTERVALS_TO_NOTES = {
|
||||
INTERVALS_TO_NOTES = MappingProxyType({
|
||||
0: "C",
|
||||
1: "Cs",
|
||||
2: "D",
|
||||
@ -84,9 +90,9 @@ INTERVALS_TO_NOTES = {
|
||||
9: "A",
|
||||
10: "Bb",
|
||||
11: "B",
|
||||
}
|
||||
})
|
||||
|
||||
CIRCLE_OF_FIFTHS = [
|
||||
CIRCLE_OF_FIFTHS = (
|
||||
"Gb",
|
||||
"Cs",
|
||||
"Ab",
|
||||
@ -100,15 +106,15 @@ CIRCLE_OF_FIFTHS = [
|
||||
"E",
|
||||
"B",
|
||||
"Fs",
|
||||
]
|
||||
)
|
||||
|
||||
MODIFIERS = {
|
||||
MODIFIERS = MappingProxyType({
|
||||
"#": 1,
|
||||
"b": -1,
|
||||
"s": 1,
|
||||
}
|
||||
})
|
||||
|
||||
ROMANS = {"i": 1, "v": 5, "x": 10, "l": 50, "c": 100, "d": 500, "m": 1000}
|
||||
ROMANS = MappingProxyType({"i": 1, "v": 5, "x": 10, "l": 50, "c": 100, "d": 500, "m": 1000})
|
||||
|
||||
# pylint: disable=locally-disabled, too-many-lines
|
||||
|
||||
|
||||
Reference in New Issue
Block a user