diff --git a/ziffers/classes.py b/ziffers/classes.py index d557d37..74f43ba 100644 --- a/ziffers/classes.py +++ b/ziffers/classes.py @@ -213,6 +213,7 @@ class Chord(Event): pitch_classes: list[Pitch] = field(default=None) notes: list[int] = field(default=None) + freqs: list[float] = field(default=None, init=False) def set_notes(self, notes: list[int]): """Set notes to the class""" @@ -221,11 +222,16 @@ class Chord(Event): def update_notes(self, options): """Update notes""" notes = [] + freqs = [] + for pitch in self.pitch_classes: pitch.update_options(options) pitch.update_note() notes.append(pitch.note) + freqs.append(pitch.freq) + self.notes = notes + self.freqs = freqs @dataclass(kw_only=True) @@ -373,7 +379,7 @@ class Sequence(Meta): def _loop_items(items, options): for item in items: - yield from _resolve_item(item, options) + yield from _resolve_item(item, options) def _update_options(current: Item, options: dict) -> dict: """Update options based on current item""" @@ -481,6 +487,7 @@ class Ziffers(Sequence): """Main class for holding options and the current state""" options: dict = field(default_factory=DEFAULT_OPTIONS) + start_options: dict = None loop_i: int = 0 iterator = None current: Item = field(default=None) @@ -505,12 +512,13 @@ class Ziffers(Sequence): else: self.options = DEFAULT_OPTIONS + self.start_options = self.options.copy() self.evaluated_values = list(self.evaluate_tree(self.options)) self.iterator = iter(self.evaluated_values) def re_eval(self, options=None): """Re-evaluate the iterator""" - self.options.update(DEFAULT_OPTIONS) + self.options = self.start_options.copy() if options: self.options.update(options) self.evaluated_values = list(self.evaluate_tree(self.options)) @@ -628,11 +636,13 @@ class Subdivision(Sequence): has_children: bool = field(default=False, init=False) def evaluate(self, options): + """Evaluate tree and then calculate lengths using subdivision""" self.evaluated_values = list(self.evaluate_tree(options.copy())) self.evaluated_values = list(self.evaluate_subdivisions(options)) return self def evaluate_subdivisions(self, options): + """Calculate new durations by dividing with the number of items in the sequence""" self.subdiv_length = len(self.evaluated_values) self.local_options = options.copy() self.local_options["duration"] = options["duration"] / self.subdiv_length @@ -785,6 +795,7 @@ class Euclid(Item): evaluated_values: list = field(default=None) def evaluate(self, options): + """Evaluate values using euclidean spread""" onset_values = [ val for val in self.onset.values if not isinstance(val, Whitespace) ] diff --git a/ziffers/common.py b/ziffers/common.py index d74529a..3aa9dfc 100644 --- a/ziffers/common.py +++ b/ziffers/common.py @@ -58,13 +58,13 @@ def string_rewrite(axiom: str, rules: dict): def euclidian_rhythm(pulses: int, length: int, rotate: int = 0): """Calculate Euclidean rhythms. Original algorithm by Thomas Morrill.""" - def _starts_descent(list, index): - length = len(list) + def _starts_descent(arr, index): + length = len(arr) next_index = (index + 1) % length - return list[index] > list[next_index] + return arr[index] > arr[next_index] - def rotation(l, n): - return l[-n:] + l[:-n] + def rotation(arr, idx): + return arr[-idx:] + arr[:-idx] if pulses >= length: return [True] diff --git a/ziffers/converters.py b/ziffers/converters.py index be40e9d..e9902c1 100644 --- a/ziffers/converters.py +++ b/ziffers/converters.py @@ -1,40 +1,44 @@ """Collection of converters""" -from music21 import converter, note, stream, meter, chord +from music21 import converter, note, stream, meter, chord, environment from ziffers import zparse, Ziffers, Pitch, Rest, Chord -def to_music21(strData: str|Ziffers, **options): + +def to_music21(expression: str | Ziffers, **options): """Helper for passing options to the parser""" converter.registerSubconverter(ZiffersMusic21) - - if isinstance(strData,Ziffers): + + if isinstance(expression, Ziffers): if options: - options["preparsed"] = strData + options["preparsed"] = expression else: - options = {"preparsed": strData} + options = {"preparsed": expression} options = {"ziffers": options} return converter.parse("PREPARSED", format="ziffers", keywords=options) if options: options = {"ziffers": options} - return converter.parse(strData, format="ziffers", keywords=options) - else: - test = converter.parse(strData, format="ziffers") - return test + return converter.parse(expression, format="ziffers", keywords=options) + + test = converter.parse(expression, format="ziffers") + return test + def set_musescore_path(path: str): """Helper for setting the Musescore path""" - us = environment.UserSettings() + settings = environment.UserSettings() # Default windows path: # 'C:\\Program Files\\MuseScore 3\\bin\\MuseScore3.exe' - us['musicxmlPath'] = path - us['musescoreDirectPNGPath'] = path + settings["musicxmlPath"] = path + settings["musescoreDirectPNGPath"] = path + class ZiffersMusic21(converter.subConverters.SubConverter): """Ziffers converter to Music21""" + registerFormats = ("ziffers",) registerInputExtensions = ("zf",) - def parseData(self, strData, number=None): + def parseData(self, dataString, number=None): """Parses Ziffers string to Music21 object""" # Look for options in keywords object keywords = self.keywords["keywords"] @@ -43,25 +47,25 @@ class ZiffersMusic21(converter.subConverters.SubConverter): if "preparsed" in options: parsed = options["preparsed"] else: - parsed = zparse(strData, **options) + parsed = zparse(dataString, **options) else: - parsed = zparse(strData) + parsed = zparse(dataString) - s = stream.Part() + note_stream = stream.Part() if "time" in options: - m = meter.TimeSignature(options["time"]) # Common time + m_item = meter.TimeSignature(options["time"]) # Common time else: - m = meter.TimeSignature("c") # Common time + m_item = meter.TimeSignature("c") # Common time - s.insert(0, m) + note_stream.insert(0, m_item) for item in parsed: - if isinstance(item,Pitch): + if isinstance(item, Pitch): m_item = note.Note(item.note) m_item.duration.quarterLength = item.duration * 4 - elif isinstance(item,Rest): + elif isinstance(item, Rest): m_item = note.Rest(item.duration * 4) - elif isinstance(item,Chord): + elif isinstance(item, Chord): m_item = chord.Chord(item.notes) m_item.duration.quarterLength = item.duration * 4 - s.append(m_item) - self.stream = s.makeMeasures() + note_stream.append(m_item) + self.stream = note_stream.makeMeasures()