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