Added parsing for monzos and support for escaped pitch_classes

Syntax for monzos supported in scala scales: [-1 1> etc.

Support for escaped pitches: {q12 e23 26}
This commit is contained in:
2023-03-16 22:29:24 +02:00
parent 882a9a7b4b
commit 5d122a90e0
11 changed files with 284 additions and 102 deletions

View File

@ -1,3 +1,3 @@
from .items import *
from .root import *
from .sequences import *
from .sequences import *

View File

@ -1,7 +1,6 @@
""" Ziffers item classes """
from dataclasses import dataclass, field, asdict
from math import floor
import operator
import random
from ..scale import (
note_from_pc,
@ -149,6 +148,26 @@ class Event(Item):
class Rest(Event):
"""Class for rests"""
def get_note(self):
"""Getter for note"""
return None
def get_freq(self):
"""Getter for freq"""
return None
def get_octave(self):
"""Getter for octave"""
return None
def get_pitch_class(self):
"""Getter for pitche"""
return None
def get_pitch_bend(self):
"""Getter for pitche"""
return None
@dataclass
class Measure(Item):
@ -283,9 +302,16 @@ class RandomPitch(Event):
Returns:
int: Returns random pitch
"""
return random.randint(
0, get_scale_length(options.get("scale", "Major")) if options else 9
)
if options:
scale = options["scale"]
if isinstance(scale, str):
scale_length = get_scale_length(options.get("scale", "Major"))
else:
scale_length = len(scale)
else:
scale_length = 9
return random.randint(0, scale_length)
@dataclass(kw_only=True)
@ -604,14 +630,6 @@ class Operator(Item):
value: ...
@dataclass(kw_only=True)
class Operation(Item):
"""Class for lisp-like operations: (+ 1 2 3) etc."""
values: list
operator: operator
@dataclass(kw_only=True)
class Atom(Item):
"""Class for evaluable atoms"""

View File

@ -3,7 +3,7 @@
from dataclasses import dataclass, field
from itertools import islice, cycle
from ..defaults import DEFAULT_OPTIONS
from .items import Item, Pitch, Chord, Event
from .items import Item, Pitch, Chord, Event, Rest
from .sequences import Sequence, Subdivision
@ -32,7 +32,7 @@ class Ziffers(Sequence):
def __len__(self):
return len(self.evaluated_values)
def __iter__(self):
return self
@ -107,15 +107,15 @@ class Ziffers(Sequence):
return [
val.get_pitch_class()
for val in self.evaluated_values
if isinstance(val, (Pitch, Chord))
if isinstance(val, (Pitch, Chord, Rest))
]
def pitch_bends(self) -> list[int]:
"""Return list of pitch bend values"""
return [
val.get_pitch_bend()
for val in self.evaluated_values
if isinstance(val, (Pitch, Chord))
if isinstance(val, (Pitch, Chord, Rest))
]
def notes(self) -> list[int]:
@ -123,7 +123,7 @@ class Ziffers(Sequence):
return [
val.get_note()
for val in self.evaluated_values
if isinstance(val, (Pitch, Chord))
if isinstance(val, (Pitch, Chord, Rest))
]
def durations(self) -> list[float]:
@ -133,10 +133,12 @@ class Ziffers(Sequence):
for val in self.evaluated_values
if isinstance(val, Event)
]
def total_duration(self) -> float:
"""Return total duration"""
return sum([val.duration for val in self.evaluated_values if isinstance(val, Event)])
return sum(
[val.duration for val in self.evaluated_values if isinstance(val, Event)]
)
def total_beats(self) -> float:
"""Return total beats"""
@ -161,7 +163,7 @@ class Ziffers(Sequence):
return [
val.get_octave()
for val in self.evaluated_values
if isinstance(val, (Pitch, Chord))
if isinstance(val, (Pitch, Chord, Rest))
]
def freqs(self) -> list[int]:
@ -169,9 +171,9 @@ class Ziffers(Sequence):
return [
val.get_freq()
for val in self.evaluated_values
if isinstance(val, (Pitch, Chord))
if isinstance(val, (Pitch, Chord, Rest))
]
def collect(self, num: int = None, keys: str | list = None) -> list:
"""Collect n items from parsed Ziffers"""
if num is None:

View File

@ -4,8 +4,9 @@ from itertools import product
from math import floor
from types import LambdaType
from copy import deepcopy
import operator
from ..defaults import DEFAULT_OPTIONS
from ..common import cyclic_zip, euclidian_rhythm
from ..common import cyclic_zip, euclidian_rhythm, flatten
from ..scale import note_from_pc, midi_to_freq
from .items import (
Meta,
@ -31,7 +32,7 @@ from .items import (
Modification,
Whitespace,
Sample,
SampleList
SampleList,
)
@ -46,6 +47,8 @@ def resolve_item(item: Meta, options: dict):
elif isinstance(item, Subdivision):
item.evaluate_values(options)
yield item
elif isinstance(item, Eval):
yield from item.evaluate_values(options)
else:
yield from item.evaluate_tree(options)
elif isinstance(item, VariableAssignment):
@ -66,14 +69,14 @@ def resolve_item(item: Meta, options: dict):
run=opt_item,
text=item.text,
kwargs=(options | item.local_options),
local_options=item.local_options
local_options=item.local_options,
)
elif isinstance(opt_item, str):
yield Sample(
name=opt_item,
text=item.text,
kwargs=(options | item.local_options),
local_options=item.local_options
local_options=item.local_options,
)
variable = deepcopy(opt_item)
yield from resolve_item(variable, options)
@ -90,7 +93,7 @@ def resolve_item(item: Meta, options: dict):
run=opt_item,
text=var.text,
kwargs=(options | var.local_options),
local_options=var.local_options
local_options=var.local_options,
)
)
elif isinstance(opt_item, str):
@ -99,7 +102,7 @@ def resolve_item(item: Meta, options: dict):
name=opt_item,
text=var.text,
kwargs=(options | var.local_options),
local_options=var.local_options
local_options=var.local_options,
)
)
elif isinstance(opt_item, Sequence):
@ -343,7 +346,7 @@ class Subdivision(Sequence):
if isinstance(item, Event):
if duration is not None:
item.duration = new_d
item.beat = new_d*4
item.beat = new_d * 4
yield item
@ -373,6 +376,8 @@ class ListOperation(Sequence):
flattened_list.extend(list(item.evaluate_durations()))
elif isinstance(item, RepeatedListSequence):
flattened_list.extend(list(item.resolve_repeat(options)))
elif isinstance(item, Eval):
flattened_list.extend(item.evaluate_values(options))
else:
flattened_list.append(_filter_operation(item, options))
elif isinstance(item, Cyclic):
@ -543,13 +548,43 @@ class ListOperation(Sequence):
class Eval(Sequence):
"""Class for evaluation notation"""
result: ... = None
wrap_start: str = field(default="{", repr=False)
wrap_end: str = field(default="}", repr=False)
def __post_init__(self):
super().__post_init__()
self.result = eval(self.text)
# def __post_init__(self):
# self.text = "".join([val.text for val in flatten(self.values)])
# super().__post_init__()
def evaluate_values(self, options):
operations = [val for val in self.values if isinstance(val, (Operation, Rest))]
eval_values = []
for val in operations:
if isinstance(val,Operation):
eval_values.append(Pitch(pitch_class=val.evaluate(), kwargs=options | val.local_options))
else:
eval_values.append(val)
self.evaluated_values = eval_values
return self.evaluated_values
@dataclass(kw_only=True)
class LispOperation(Sequence):
"""Class for lisp-like operations: (+ 1 2 3) etc."""
values: list
operator: operator
@dataclass(kw_only=True)
class Operation(Sequence):
"""Class for sequential operations"""
values: list
def evaluate(self):
return eval(self.text)
@dataclass(kw_only=True)
@ -588,6 +623,8 @@ class RepeatedSequence(Sequence):
elif isinstance(item, Subdivision):
item.evaluate_values(options)
yield item
elif isinstance(item, Eval):
yield from item.evaluate_values(options)
else:
yield from item
elif isinstance(item, Cyclic):