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:
@ -1,3 +1,3 @@
|
||||
from .items import *
|
||||
from .root import *
|
||||
from .sequences import *
|
||||
from .sequences import *
|
||||
|
||||
@ -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"""
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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):
|
||||
|
||||
Reference in New Issue
Block a user