Added rest and infinite indexing
Preliminary support for rest: q 1 r 3 er 4 etc. Currently supports only single character prefixes like: qr er.
This commit is contained in:
@ -88,6 +88,9 @@ class Event(Item):
|
|||||||
|
|
||||||
duration: float = field(default=None)
|
duration: float = field(default=None)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Rest(Event):
|
||||||
|
"""Class for rests"""
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class Pitch(Event):
|
class Pitch(Event):
|
||||||
@ -136,7 +139,6 @@ class Pitch(Event):
|
|||||||
if edit:
|
if edit:
|
||||||
self.update_note(True)
|
self.update_note(True)
|
||||||
|
|
||||||
|
|
||||||
def set_note(self, note: int) -> int:
|
def set_note(self, note: int) -> int:
|
||||||
"""Sets a note for the pitch and returns the note.
|
"""Sets a note for the pitch and returns the note.
|
||||||
|
|
||||||
@ -175,7 +177,9 @@ class RandomPitch(Event):
|
|||||||
Returns:
|
Returns:
|
||||||
int: Returns random pitch
|
int: Returns random pitch
|
||||||
"""
|
"""
|
||||||
return random.randint(0, get_scale_length(options.get("scale","Major")) if options else 9)
|
return random.randint(
|
||||||
|
0, get_scale_length(options.get("scale", "Major")) if options else 9
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
@ -205,6 +209,7 @@ class Chord(Event):
|
|||||||
notes.append(pitch.note)
|
notes.append(pitch.note)
|
||||||
self.notes = notes
|
self.notes = notes
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class RomanNumeral(Event):
|
class RomanNumeral(Event):
|
||||||
"""Class for roman numbers"""
|
"""Class for roman numbers"""
|
||||||
@ -257,7 +262,7 @@ class Sequence(Meta):
|
|||||||
self.text = self.__collect_text()
|
self.text = self.__collect_text()
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
return self.evaluated_values[index % len(self.evaluated_values)]
|
return self.values[index]
|
||||||
|
|
||||||
def update_values(self, new_values):
|
def update_values(self, new_values):
|
||||||
"""Update value attributes from dict"""
|
"""Update value attributes from dict"""
|
||||||
@ -317,6 +322,7 @@ class Sequence(Meta):
|
|||||||
for item in tree:
|
for item in tree:
|
||||||
yield from _resolve_item(item, options)
|
yield from _resolve_item(item, options)
|
||||||
|
|
||||||
|
# TODO: Refactor types to isinstance?
|
||||||
def _update_options(current: Item, options: dict) -> dict:
|
def _update_options(current: Item, options: dict) -> dict:
|
||||||
"""Update options based on current item"""
|
"""Update options based on current item"""
|
||||||
if current.item_type == "change": # Change options
|
if current.item_type == "change": # Change options
|
||||||
@ -374,18 +380,28 @@ class Sequence(Meta):
|
|||||||
chord_notes = []
|
chord_notes = []
|
||||||
for note in current.notes:
|
for note in current.notes:
|
||||||
pitch_dict = midi_to_pitch_class(note, key, scale)
|
pitch_dict = midi_to_pitch_class(note, key, scale)
|
||||||
pitch_classes.append(Pitch(pitch_class=pitch_dict["pitch_class"],kwargs=(pitch_dict | options)))
|
pitch_classes.append(
|
||||||
|
Pitch(
|
||||||
|
pitch_class=pitch_dict["pitch_class"],
|
||||||
|
kwargs=(pitch_dict | options),
|
||||||
|
)
|
||||||
|
)
|
||||||
pitch_text += pitch_dict["text"]
|
pitch_text += pitch_dict["text"]
|
||||||
chord_notes.append(note_from_pc(
|
chord_notes.append(
|
||||||
|
note_from_pc(
|
||||||
root=key,
|
root=key,
|
||||||
pitch_class=pitch_dict["pitch_class"],
|
pitch_class=pitch_dict["pitch_class"],
|
||||||
intervals=scale,
|
intervals=scale,
|
||||||
modifier=pitch_dict.get("modifier", 0),
|
modifier=pitch_dict.get("modifier", 0),
|
||||||
octave=pitch_dict.get("octave",0)
|
octave=pitch_dict.get("octave", 0),
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
chord = Chord(
|
chord = Chord(
|
||||||
text=pitch_text, pitch_classes=pitch_classes, notes=chord_notes, kwargs=options
|
text=pitch_text,
|
||||||
|
pitch_classes=pitch_classes,
|
||||||
|
notes=chord_notes,
|
||||||
|
kwargs=options,
|
||||||
)
|
)
|
||||||
return chord
|
return chord
|
||||||
|
|
||||||
@ -394,6 +410,9 @@ class Sequence(Meta):
|
|||||||
if set(("key", "scale")) <= options.keys():
|
if set(("key", "scale")) <= options.keys():
|
||||||
if isinstance(item, Pitch):
|
if isinstance(item, Pitch):
|
||||||
item.update_options(options)
|
item.update_options(options)
|
||||||
|
item.update_note()
|
||||||
|
if isinstance(item,Rest):
|
||||||
|
item.update_options(options)
|
||||||
elif isinstance(item, (RandomPitch, RandomInteger)):
|
elif isinstance(item, (RandomPitch, RandomInteger)):
|
||||||
item = _create_pitch(item, options)
|
item = _create_pitch(item, options)
|
||||||
elif isinstance(item, Chord):
|
elif isinstance(item, Chord):
|
||||||
@ -420,7 +439,6 @@ class Sequence(Meta):
|
|||||||
self, values=[item for item in self.values if isinstance(item, keep)]
|
self, values=[item for item in self.values if isinstance(item, keep)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class Ziffers(Sequence):
|
class Ziffers(Sequence):
|
||||||
"""Main class for holding options and the current state"""
|
"""Main class for holding options and the current state"""
|
||||||
@ -430,6 +448,9 @@ class Ziffers(Sequence):
|
|||||||
iterator = None
|
iterator = None
|
||||||
current: Item = field(default=None)
|
current: Item = field(default=None)
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
return self.evaluated_values[index % len(self.evaluated_values)]
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from .classes import (
|
|||||||
OctaveChange,
|
OctaveChange,
|
||||||
OctaveAdd,
|
OctaveAdd,
|
||||||
Pitch,
|
Pitch,
|
||||||
|
Rest,
|
||||||
RandomPitch,
|
RandomPitch,
|
||||||
RandomPercent,
|
RandomPercent,
|
||||||
Chord,
|
Chord,
|
||||||
@ -37,19 +38,31 @@ from .scale import parse_roman, chord_from_roman_numeral
|
|||||||
class ZiffersTransformer(Transformer):
|
class ZiffersTransformer(Transformer):
|
||||||
"""Rules for transforming Ziffers expressions into tree."""
|
"""Rules for transforming Ziffers expressions into tree."""
|
||||||
|
|
||||||
def __init__(self, options: Optional[dict] = None):
|
|
||||||
super().__init__()
|
|
||||||
self.options = options
|
|
||||||
|
|
||||||
def start(self, items) -> Ziffers:
|
def start(self, items) -> Ziffers:
|
||||||
"""Root for the rules"""
|
"""Root for the rules"""
|
||||||
# seq = Sequence(values=items[0])
|
|
||||||
return Ziffers(values=items[0], options={})
|
return Ziffers(values=items[0], options={})
|
||||||
|
|
||||||
def sequence(self, items):
|
def sequence(self, items):
|
||||||
"""Flatten sequence"""
|
"""Flatten sequence"""
|
||||||
return flatten(items)
|
return flatten(items)
|
||||||
|
|
||||||
|
def rest(self, items):
|
||||||
|
"""Return duration event"""
|
||||||
|
if len(items)>0:
|
||||||
|
chars = items[0]
|
||||||
|
val = DEFAULT_DURS[chars[0]]
|
||||||
|
# TODO: Add support for dots
|
||||||
|
#if len(chars)>1:
|
||||||
|
# dots = len(chars)-1
|
||||||
|
# val = val * (2.0 - (1.0 / (2 * dots)))
|
||||||
|
return Rest(text=chars+"r", duration=val)
|
||||||
|
return Rest(text="r")
|
||||||
|
|
||||||
|
return Rest(text=chars+"r", duration=val)
|
||||||
|
|
||||||
|
def rest_duration(self,items):
|
||||||
|
return items[0].value
|
||||||
|
|
||||||
def random_integer(self, item) -> RandomInteger:
|
def random_integer(self, item) -> RandomInteger:
|
||||||
"""Parses random integer syntax"""
|
"""Parses random integer syntax"""
|
||||||
val = item[0][1:-1].split(",")
|
val = item[0][1:-1].split(",")
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Root for the rules
|
// Root for the rules
|
||||||
?root: sequence -> start
|
?root: sequence -> start
|
||||||
sequence: (pitch_class | dur_change | oct_mod | oct_change | WS | chord | named_roman | cycle | random_integer | random_pitch | random_percent | range | list | repeated_list | lisp_operation | list_op | subdivision | eval | euclid | repeat)*
|
sequence: (pitch_class | rest | dur_change | oct_mod | oct_change | WS | chord | named_roman | cycle | random_integer | random_pitch | random_percent | range | list | repeated_list | lisp_operation | list_op | subdivision | eval | euclid | repeat)*
|
||||||
|
|
||||||
// Pitch classes
|
// Pitch classes
|
||||||
pitch_class: prefix* pitch
|
pitch_class: prefix* pitch
|
||||||
@ -11,6 +11,18 @@
|
|||||||
octave: /[_^]+/
|
octave: /[_^]+/
|
||||||
modifier: /[#b]/
|
modifier: /[#b]/
|
||||||
|
|
||||||
|
// Durations
|
||||||
|
// TODO: Refactor dchar as: /([mklpdcwyhnqaefsxtgujzo](\.)*)(?=\d)/
|
||||||
|
duration_chars: dotted_dur+
|
||||||
|
dotted_dur: dchar dot*
|
||||||
|
decimal: /-?[0-9]+\.[0-9]+/
|
||||||
|
dchar: /[mklpdcwyhnqaefsxtgujzo]/
|
||||||
|
dot: "."
|
||||||
|
|
||||||
|
rest: rest_duration? "r"
|
||||||
|
// TODO: Refactor (\.)* when other durchars uses lookaheads
|
||||||
|
rest_duration: /([mklpdcwyhnqaefsxtgujzo])(?=r)/
|
||||||
|
|
||||||
// Chords
|
// Chords
|
||||||
chord: pitch_class pitch_class+
|
chord: pitch_class pitch_class+
|
||||||
named_roman: roman_number (("^" chord_name))? // TODO: Add | ("+" number)
|
named_roman: roman_number (("^" chord_name))? // TODO: Add | ("+" number)
|
||||||
@ -34,22 +46,14 @@
|
|||||||
operator: /([\+\-\*\/%]|<<|>>)/
|
operator: /([\+\-\*\/%]|<<|>>)/
|
||||||
|
|
||||||
// Euclidean cycles
|
// Euclidean cycles
|
||||||
|
// TODO: Support randomization etc.
|
||||||
|
//euclid_operator: (">" | "<") number "," number ["," number] (">" | "<")
|
||||||
euclid: list euclid_operator list?
|
euclid: list euclid_operator list?
|
||||||
?euclid_operator: /<[0-9]+,[0-9]+(,[0-9])?>/
|
?euclid_operator: /<[0-9]+,[0-9]+(,[0-9])?>/
|
||||||
|
|
||||||
// TODO: Support randomization etc.
|
|
||||||
//euclid_operator: (">" | "<") number "," number ["," number] (">" | "<")
|
|
||||||
|
|
||||||
// Lisp like list operation
|
// Lisp like list operation
|
||||||
lisp_operation: "(" operator WS sequence ")"
|
lisp_operation: "(" operator WS sequence ")"
|
||||||
|
|
||||||
// Durations
|
|
||||||
duration_chars: dotted_dur+
|
|
||||||
dotted_dur: dchar dot*
|
|
||||||
decimal: /-?[0-9]+\.[0-9]+/
|
|
||||||
dchar: /[mklpdcwyhnqaefsxtgujzo]/
|
|
||||||
dot: "."
|
|
||||||
|
|
||||||
// Subdivision
|
// Subdivision
|
||||||
subdivision: "[" subitems "]"
|
subdivision: "[" subitems "]"
|
||||||
subitems: (pitch_class | WS | chord | cycle | subdivision)*
|
subitems: (pitch_class | WS | chord | cycle | subdivision)*
|
||||||
@ -59,7 +63,7 @@
|
|||||||
oct_change: escaped_octave WS
|
oct_change: escaped_octave WS
|
||||||
dur_change: (decimal | char_change)
|
dur_change: (decimal | char_change)
|
||||||
char_change: dchar_not_prefix+
|
char_change: dchar_not_prefix+
|
||||||
dchar_not_prefix: /([mklpdcwyhnqaefsxtgujzo](\.)*)(?!\d)/
|
dchar_not_prefix: /([mklpdcwyhnqaefsxtgujzo](\.)*)(?![\dr])/
|
||||||
|
|
||||||
// Generative rules
|
// Generative rules
|
||||||
random_integer: /\(-?[0-9]+,-?[0-9]+\)/
|
random_integer: /\(-?[0-9]+,-?[0-9]+\)/
|
||||||
|
|||||||
Reference in New Issue
Block a user