run black on codebase
This commit is contained in:
56
main.py
56
main.py
@ -3,34 +3,34 @@ from rich import print
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
expressions = {
|
expressions = {
|
||||||
'Pitches': "-2 -1 0 1 2",
|
"Pitches": "-2 -1 0 1 2",
|
||||||
'Chords': "0 024 2 246",
|
"Chords": "0 024 2 246",
|
||||||
'Note lengths': "w 0 h 1 q 2 e 3 s 4",
|
"Note lengths": "w 0 h 1 q 2 e 3 s 4",
|
||||||
'Subdivision': "[1 2 [3 4]]",
|
"Subdivision": "[1 2 [3 4]]",
|
||||||
'Decimal durations': "0.25 0 1 <0.333>2 3",
|
"Decimal durations": "0.25 0 1 <0.333>2 3",
|
||||||
'Octaves': "^ 0 ^ 1 _ 2 _ 3",
|
"Octaves": "^ 0 ^ 1 _ 2 _ 3",
|
||||||
'Escaped octave': "<2> 1 <1>1<-2>3",
|
"Escaped octave": "<2> 1 <1>1<-2>3",
|
||||||
'Roman chords': "i ii iii+4 iv+5 v+8 vi+10 vii+20",
|
"Roman chords": "i ii iii+4 iv+5 v+8 vi+10 vii+20",
|
||||||
'Named chords': "i^7 i^min i^dim i^maj7",
|
"Named chords": "i^7 i^min i^dim i^maj7",
|
||||||
'Modal interchange (a-g)': "iiia ig ivf^7",
|
"Modal interchange (a-g)": "iiia ig ivf^7",
|
||||||
'Escape/eval': "{10 11} {1.2 2.43} {3+1*2}",
|
"Escape/eval": "{10 11} {1.2 2.43} {3+1*2}",
|
||||||
'Randoms': "% ? % ? % ?",
|
"Randoms": "% ? % ? % ?",
|
||||||
'Random between': "(-3,6)",
|
"Random between": "(-3,6)",
|
||||||
'Random selections': "[q 1 2, q 3 e 4 6]",
|
"Random selections": "[q 1 2, q 3 e 4 6]",
|
||||||
'Repeat': "[: 1 (2,6) 3 :4]",
|
"Repeat": "[: 1 (2,6) 3 :4]",
|
||||||
'Repeat cycles': "[: <q e> (1,4) <(2 3) (3 (1,7))> :]",
|
"Repeat cycles": "[: <q e> (1,4) <(2 3) (3 (1,7))> :]",
|
||||||
'Lists': "h 1 q(0 1 2 3) 2",
|
"Lists": "h 1 q(0 1 2 3) 2",
|
||||||
'List cycles': "(: <q e> (1,4) <(2 3) (3 (1,7))> :)",
|
"List cycles": "(: <q e> (1,4) <(2 3) (3 (1,7))> :)",
|
||||||
'Loop cycles (for zloop or z0-z9)': "<0 <1 <2 <3 <4 5>>>>>",
|
"Loop cycles (for zloop or z0-z9)": "<0 <1 <2 <3 <4 5>>>>>",
|
||||||
'Basic operations': "(1 2 (3 4)+2)*2 ((1 2 3)+(0 9 13))-2 ((3 4 {10})*(2 9 3))%7",
|
"Basic operations": "(1 2 (3 4)+2)*2 ((1 2 3)+(0 9 13))-2 ((3 4 {10})*(2 9 3))%7",
|
||||||
'Product operations': "(0 1 2 3)+(1 4 2 3) (0 1 2)-(0 2 1)+2",
|
"Product operations": "(0 1 2 3)+(1 4 2 3) (0 1 2)-(0 2 1)+2",
|
||||||
'Euclid cycles': "(q1)<6,7>(q4 (e3 e4) q2) (q1)<6,7>(q4 q3 q2)",
|
"Euclid cycles": "(q1)<6,7>(q4 (e3 e4) q2) (q1)<6,7>(q4 q3 q2)",
|
||||||
'Transformations': "(0 1 2)<r> (0 1 2)<i>(-2 1)",
|
"Transformations": "(0 1 2)<r> (0 1 2)<i>(-2 1)",
|
||||||
'List assignation': "A=(0 (1,6) 3) B=(3 ? 2) B A B B A",
|
"List assignation": "A=(0 (1,6) 3) B=(3 ? 2) B A B B A",
|
||||||
'Random repeat': "(: 1 (2,6) 3 :4)",
|
"Random repeat": "(: 1 (2,6) 3 :4)",
|
||||||
'Conditionals': "1 {%<0.5?3} 3 4 (: 1 2 {%<0.2?3:2} :3)",
|
"Conditionals": "1 {%<0.5?3} 3 4 (: 1 2 {%<0.2?3:2} :3)",
|
||||||
'Functions': "(0 1 2 3){x%3==0?x-2:x+2}",
|
"Functions": "(0 1 2 3){x%3==0?x-2:x+2}",
|
||||||
'Polynomials': "(-10..10){(x**3)*(x+1)%12}",
|
"Polynomials": "(-10..10){(x**3)*(x+1)%12}",
|
||||||
}
|
}
|
||||||
for ex in expressions:
|
for ex in expressions:
|
||||||
try:
|
try:
|
||||||
|
|||||||
4
repl.py
4
repl.py
@ -1,12 +1,12 @@
|
|||||||
from ziffers import *
|
from ziffers import *
|
||||||
from rich import print
|
from rich import print
|
||||||
|
|
||||||
EXIT_CONDITION = ('exit', 'quit', '')
|
EXIT_CONDITION = ("exit", "quit", "")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print(f"[purple]== ZIFFERS REPL ==[/purple]")
|
print(f"[purple]== ZIFFERS REPL ==[/purple]")
|
||||||
while True:
|
while True:
|
||||||
expr = input('> ')
|
expr = input("> ")
|
||||||
if expr not in EXIT_CONDITION:
|
if expr not in EXIT_CONDITION:
|
||||||
try:
|
try:
|
||||||
result = parse_expression(expr)
|
result = parse_expression(expr)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from ziffers import *
|
from ziffers import *
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
def test_can_parse():
|
def test_can_parse():
|
||||||
expressions = [
|
expressions = [
|
||||||
"[1 [2 3]]",
|
"[1 [2 3]]",
|
||||||
@ -21,10 +22,11 @@ def test_can_parse():
|
|||||||
print(e)
|
print(e)
|
||||||
results.append(False)
|
results.append(False)
|
||||||
|
|
||||||
# Return true if all the results are true (succesfully parsed)
|
# Return true if all the results are true (succesfully parsed)
|
||||||
print(results)
|
print(results)
|
||||||
assert all(results)
|
assert all(results)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"pattern",
|
"pattern",
|
||||||
[
|
[
|
||||||
@ -35,6 +37,7 @@ def test_can_parse():
|
|||||||
def test_parsing_text(pattern: str):
|
def test_parsing_text(pattern: str):
|
||||||
assert parse_expression(pattern).text == pattern
|
assert parse_expression(pattern).text == pattern
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"pattern,expected",
|
"pattern,expected",
|
||||||
[
|
[
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from ziffers import scale
|
from ziffers import scale
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"name,expected",
|
"name,expected",
|
||||||
[
|
[
|
||||||
@ -14,11 +15,37 @@ import pytest
|
|||||||
def test_notenames(name: str, expected: int):
|
def test_notenames(name: str, expected: int):
|
||||||
assert scale.note_to_midi(name) == expected
|
assert scale.note_to_midi(name) == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"pcs,expected",
|
"pcs,expected",
|
||||||
[
|
[
|
||||||
(list(range(-9,10)), [45, 47, 48, 50, 52, 53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76]),
|
(
|
||||||
|
list(range(-9, 10)),
|
||||||
|
[
|
||||||
|
45,
|
||||||
|
47,
|
||||||
|
48,
|
||||||
|
50,
|
||||||
|
52,
|
||||||
|
53,
|
||||||
|
55,
|
||||||
|
57,
|
||||||
|
59,
|
||||||
|
60,
|
||||||
|
62,
|
||||||
|
64,
|
||||||
|
65,
|
||||||
|
67,
|
||||||
|
69,
|
||||||
|
71,
|
||||||
|
72,
|
||||||
|
74,
|
||||||
|
76,
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_note_to_midi(pcs: str, expected: int):
|
def test_note_to_midi(pcs: str, expected: int):
|
||||||
assert [scale.note_from_pc(root=60, pitch_class=val, intervals="Ionian") for val in pcs] == expected
|
assert [
|
||||||
|
scale.note_from_pc(root=60, pitch_class=val, intervals="Ionian") for val in pcs
|
||||||
|
] == expected
|
||||||
|
|||||||
@ -2,81 +2,108 @@ from dataclasses import dataclass, field
|
|||||||
import operator
|
import operator
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Meta:
|
class Meta:
|
||||||
''' Abstract class for all Ziffers items'''
|
"""Abstract class for all Ziffers items"""
|
||||||
|
|
||||||
def update(self, new_values):
|
def update(self, new_values):
|
||||||
''' Update attributes from dict '''
|
"""Update attributes from dict"""
|
||||||
for key, value in new_values.items():
|
for key, value in new_values.items():
|
||||||
if hasattr(self, key):
|
if hasattr(self, key):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Item(Meta):
|
class Item(Meta):
|
||||||
''' Class for all Ziffers text based items '''
|
"""Class for all Ziffers text based items"""
|
||||||
|
|
||||||
text: str
|
text: str
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Whitespace(Item):
|
class Whitespace(Item):
|
||||||
''' Class for whitespace '''
|
"""Class for whitespace"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class DurationChange(Item):
|
class DurationChange(Item):
|
||||||
''' Class for changing duration '''
|
"""Class for changing duration"""
|
||||||
|
|
||||||
dur: float
|
dur: float
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class OctaveChange(Item):
|
class OctaveChange(Item):
|
||||||
''' Class for changing octave '''
|
"""Class for changing octave"""
|
||||||
|
|
||||||
oct: int
|
oct: int
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class OctaveMod(Item):
|
class OctaveMod(Item):
|
||||||
''' Class for modifying octave '''
|
"""Class for modifying octave"""
|
||||||
|
|
||||||
oct: int
|
oct: int
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Event(Item):
|
class Event(Item):
|
||||||
''' Abstract class for events with duration '''
|
"""Abstract class for events with duration"""
|
||||||
|
|
||||||
dur: float = None
|
dur: float = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Pitch(Event):
|
class Pitch(Event):
|
||||||
''' Class for pitch in time '''
|
"""Class for pitch in time"""
|
||||||
|
|
||||||
pc: int = None
|
pc: int = None
|
||||||
dur: float = None
|
dur: float = None
|
||||||
oct: int = None
|
oct: int = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RandomPitch(Event):
|
class RandomPitch(Event):
|
||||||
''' Class for random pitch '''
|
"""Class for random pitch"""
|
||||||
|
|
||||||
pc: int = None
|
pc: int = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RandomPercent(Item):
|
class RandomPercent(Item):
|
||||||
''' Class for random percent '''
|
"""Class for random percent"""
|
||||||
|
|
||||||
percent: float = None
|
percent: float = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Chord(Event):
|
class Chord(Event):
|
||||||
''' Class for chords '''
|
"""Class for chords"""
|
||||||
|
|
||||||
pcs: list[Pitch] = None
|
pcs: list[Pitch] = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Function(Event):
|
class Function(Event):
|
||||||
''' Class for functions '''
|
"""Class for functions"""
|
||||||
|
|
||||||
run: str = None
|
run: str = None
|
||||||
|
|
||||||
|
|
||||||
class dataclass_property(property): # pylint: disable=invalid-name
|
class dataclass_property(property): # pylint: disable=invalid-name
|
||||||
''' Hack for dataclass setters '''
|
"""Hack for dataclass setters"""
|
||||||
|
|
||||||
def __set__(self, __obj: Any, __value: Any) -> None:
|
def __set__(self, __obj: Any, __value: Any) -> None:
|
||||||
if isinstance(__value, self.__class__):
|
if isinstance(__value, self.__class__):
|
||||||
return None
|
return None
|
||||||
return super().__set__(__obj, __value)
|
return super().__set__(__obj, __value)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Sequence(Meta):
|
class Sequence(Meta):
|
||||||
''' Class for sequences of items'''
|
"""Class for sequences of items"""
|
||||||
|
|
||||||
values: list[Item]
|
values: list[Item]
|
||||||
text: str = field(init=False)
|
text: str = field(init=False)
|
||||||
_text: str = field(default=None, init=False, repr=False)
|
_text: str = field(default=None, init=False, repr=False)
|
||||||
@ -96,7 +123,7 @@ class Sequence(Meta):
|
|||||||
self.text = self.collect_text()
|
self.text = self.collect_text()
|
||||||
|
|
||||||
def update_values(self, new_values):
|
def update_values(self, new_values):
|
||||||
''' Update value attributes from dict '''
|
"""Update value attributes from dict"""
|
||||||
for key, value in new_values.items():
|
for key, value in new_values.items():
|
||||||
for obj in self.values:
|
for obj in self.values:
|
||||||
if key != "text" and hasattr(obj, key):
|
if key != "text" and hasattr(obj, key):
|
||||||
@ -119,27 +146,35 @@ class Sequence(Meta):
|
|||||||
def pairs(self) -> list[tuple]:
|
def pairs(self) -> list[tuple]:
|
||||||
return [(val.pc, val.dur) for val in self.values if type(val) is Pitch]
|
return [(val.pc, val.dur) for val in self.values if type(val) is Pitch]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ListSequence(Sequence):
|
class ListSequence(Sequence):
|
||||||
''' Class for Ziffers list sequences '''
|
"""Class for Ziffers list sequences"""
|
||||||
|
|
||||||
wrap_start: str = field(default="(", repr=False)
|
wrap_start: str = field(default="(", repr=False)
|
||||||
wrap_end: str = field(default=")", repr=False)
|
wrap_end: str = field(default=")", repr=False)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RepeatedListSequence(Sequence):
|
class RepeatedListSequence(Sequence):
|
||||||
''' Class for Ziffers list sequences '''
|
"""Class for Ziffers list sequences"""
|
||||||
|
|
||||||
repeats: Item = None
|
repeats: Item = None
|
||||||
wrap_start: str = field(default="(:", repr=False)
|
wrap_start: str = field(default="(:", repr=False)
|
||||||
wrap_end: str = field(default=":)", repr=False)
|
wrap_end: str = field(default=":)", repr=False)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Subdivision(Item):
|
class Subdivision(Item):
|
||||||
''' Class for subdivisions '''
|
"""Class for subdivisions"""
|
||||||
|
|
||||||
values: list[Event]
|
values: list[Event]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Cyclic(Sequence):
|
class Cyclic(Sequence):
|
||||||
''' Class for cyclic sequences'''
|
"""Class for cyclic sequences"""
|
||||||
|
|
||||||
cycle: int = 0
|
cycle: int = 0
|
||||||
wrap_start: str = field(default="<", repr=False)
|
wrap_start: str = field(default="<", repr=False)
|
||||||
wrap_end: str = field(default=">", repr=False)
|
wrap_end: str = field(default=">", repr=False)
|
||||||
@ -149,78 +184,100 @@ class Cyclic(Sequence):
|
|||||||
# TODO: Do spaced need to be filtered out?
|
# TODO: Do spaced need to be filtered out?
|
||||||
self.values = [val for val in self.values if type(val) != Item]
|
self.values = [val for val in self.values if type(val) != Item]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RandomInteger(Item):
|
class RandomInteger(Item):
|
||||||
''' Class for random integer '''
|
"""Class for random integer"""
|
||||||
|
|
||||||
min: int
|
min: int
|
||||||
max: int
|
max: int
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Range(Item):
|
class Range(Item):
|
||||||
''' Class for range '''
|
"""Class for range"""
|
||||||
|
|
||||||
start: int
|
start: int
|
||||||
end: int
|
end: int
|
||||||
|
|
||||||
|
|
||||||
ops = {
|
ops = {
|
||||||
'+' : operator.add,
|
"+": operator.add,
|
||||||
'-' : operator.sub,
|
"-": operator.sub,
|
||||||
'*' : operator.mul,
|
"*": operator.mul,
|
||||||
'/' : operator.truediv,
|
"/": operator.truediv,
|
||||||
'%' : operator.mod
|
"%": operator.mod,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Operator(Item):
|
class Operator(Item):
|
||||||
''' Class for math operators '''
|
"""Class for math operators"""
|
||||||
|
|
||||||
value: ... = field(init=False, repr=False)
|
value: ... = field(init=False, repr=False)
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
self.value = ops[self.text]
|
self.value = ops[self.text]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ListOperation(Sequence):
|
class ListOperation(Sequence):
|
||||||
''' Class for list operations '''
|
"""Class for list operations"""
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Operation(Item):
|
class Operation(Item):
|
||||||
''' Class for lisp-like operations: (+ 1 2 3) etc. '''
|
"""Class for lisp-like operations: (+ 1 2 3) etc."""
|
||||||
|
|
||||||
values: list
|
values: list
|
||||||
operator: operator
|
operator: operator
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Eval(Sequence):
|
class Eval(Sequence):
|
||||||
''' Class for evaluation notation '''
|
"""Class for evaluation notation"""
|
||||||
|
|
||||||
result: ... = None
|
result: ... = None
|
||||||
wrap_start: str = field(default="{", repr=False)
|
wrap_start: str = field(default="{", repr=False)
|
||||||
wrap_end: str = field(default="}", repr=False)
|
wrap_end: str = field(default="}", repr=False)
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
super().__post_init__()
|
super().__post_init__()
|
||||||
self.result = eval(self.text)
|
self.result = eval(self.text)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Atom(Item):
|
class Atom(Item):
|
||||||
''' Class for evaluable atoms'''
|
"""Class for evaluable atoms"""
|
||||||
|
|
||||||
value: ...
|
value: ...
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Integer(Item):
|
class Integer(Item):
|
||||||
''' Class for integers '''
|
"""Class for integers"""
|
||||||
|
|
||||||
value: int
|
value: int
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Euclid(Item):
|
class Euclid(Item):
|
||||||
''' Class for euclidean cycles '''
|
"""Class for euclidean cycles"""
|
||||||
|
|
||||||
pulses: int
|
pulses: int
|
||||||
length: int
|
length: int
|
||||||
onset: list
|
onset: list
|
||||||
offset: list = None
|
offset: list = None
|
||||||
rotate: int = None
|
rotate: int = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RepeatedSequence(Sequence):
|
class RepeatedSequence(Sequence):
|
||||||
''' Class for repeats '''
|
"""Class for repeats"""
|
||||||
|
|
||||||
repeats: Item = None
|
repeats: Item = None
|
||||||
wrap_start: str = field(default="[:", repr=False)
|
wrap_start: str = field(default="[:", repr=False)
|
||||||
wrap_end: str = field(default=":]", repr=False)
|
wrap_end: str = field(default=":]", repr=False)
|
||||||
|
|
||||||
@ -1,9 +1,14 @@
|
|||||||
def flatten(arr) -> list:
|
def flatten(arr) -> list:
|
||||||
''' Flattens array'''
|
"""Flattens array"""
|
||||||
return flatten(arr[0]) + (flatten(arr[1:]) if len(arr) > 1 else []) if type(arr) is list else [arr]
|
return (
|
||||||
|
flatten(arr[0]) + (flatten(arr[1:]) if len(arr) > 1 else [])
|
||||||
|
if type(arr) is list
|
||||||
|
else [arr]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def sum_dict(arr) -> dict:
|
def sum_dict(arr) -> dict:
|
||||||
''' Sums array of dicts: [{a:3,b:3},{b:1}] -> {a:3,b:4} '''
|
"""Sums array of dicts: [{a:3,b:3},{b:1}] -> {a:3,b:4}"""
|
||||||
result = arr[0]
|
result = arr[0]
|
||||||
for hash in arr[1:]:
|
for hash in arr[1:]:
|
||||||
for key in hash.keys():
|
for key in hash.keys():
|
||||||
|
|||||||
@ -1,37 +1,37 @@
|
|||||||
default_durs = {
|
default_durs = {
|
||||||
'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
|
||||||
'd.': 3.0, #
|
"d.": 3.0, #
|
||||||
'p': 5120/1920, # ~2.666
|
"p": 5120 / 1920, # ~2.666
|
||||||
'd': 2.0, # 3840/1920
|
"d": 2.0, # 3840/1920
|
||||||
'w.': 1.5, # 2280/1920
|
"w.": 1.5, # 2280/1920
|
||||||
'c': 2560/1920, # ~1.333
|
"c": 2560 / 1920, # ~1.333
|
||||||
'w': 1.0, # 1920/1920
|
"w": 1.0, # 1920/1920
|
||||||
'h..': 0.875, # 1680/1920
|
"h..": 0.875, # 1680/1920
|
||||||
'h.': 0.75, # 1440/1920
|
"h.": 0.75, # 1440/1920
|
||||||
'y': 1280/1920, # ~0.666
|
"y": 1280 / 1920, # ~0.666
|
||||||
'h': 0.5, # 960/1920 - 1/2
|
"h": 0.5, # 960/1920 - 1/2
|
||||||
'q..': 840/1920, # ~0.4375
|
"q..": 840 / 1920, # ~0.4375
|
||||||
'q.': 0.375, # 720/1920
|
"q.": 0.375, # 720/1920
|
||||||
'n': 640/1920, # ~0.333
|
"n": 640 / 1920, # ~0.333
|
||||||
'q': 0.25, # 480/1920 - 1/4
|
"q": 0.25, # 480/1920 - 1/4
|
||||||
'e..': 420/1920, # = 0.218
|
"e..": 420 / 1920, # = 0.218
|
||||||
'e.': 0.1875, # 360/1920
|
"e.": 0.1875, # 360/1920
|
||||||
'a': 320/1920, # 0.167 - 1/8
|
"a": 320 / 1920, # 0.167 - 1/8
|
||||||
'e': 0.125, # 240/1920
|
"e": 0.125, # 240/1920
|
||||||
's..': 210/1920, # ~0.10937
|
"s..": 210 / 1920, # ~0.10937
|
||||||
's.': 180/1920, # ~0.0937
|
"s.": 180 / 1920, # ~0.0937
|
||||||
'f': 160/1920, # ~0.083 - 1/16
|
"f": 160 / 1920, # ~0.083 - 1/16
|
||||||
's': 0.0625, # 120/1920
|
"s": 0.0625, # 120/1920
|
||||||
't..': 105/1920, # ~0.0546
|
"t..": 105 / 1920, # ~0.0546
|
||||||
't.': 90/1920, # ~0.0468
|
"t.": 90 / 1920, # ~0.0468
|
||||||
'x': 80/1920, # ~0.042 - 1/32
|
"x": 80 / 1920, # ~0.042 - 1/32
|
||||||
't': 60/1920, # ~0.031
|
"t": 60 / 1920, # ~0.031
|
||||||
'u.': 45/1920, # ~0.023
|
"u.": 45 / 1920, # ~0.023
|
||||||
'g': 40/1920, # ~0.021 - 1/64
|
"g": 40 / 1920, # ~0.021 - 1/64
|
||||||
'u': 30/1920, # ~0.016
|
"u": 30 / 1920, # ~0.016
|
||||||
'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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,8 @@ from .common import flatten, sum_dict
|
|||||||
from .defaults import default_durs
|
from .defaults import default_durs
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
class ZiffersTransformer(Transformer):
|
|
||||||
|
|
||||||
|
class ZiffersTransformer(Transformer):
|
||||||
def start(self, items):
|
def start(self, items):
|
||||||
return Sequence(values=items[0])
|
return Sequence(values=items[0])
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ class ZiffersTransformer(Transformer):
|
|||||||
return Cyclic(values=values)
|
return Cyclic(values=values)
|
||||||
|
|
||||||
def pc(self, s):
|
def pc(self, s):
|
||||||
if(len(s)>1):
|
if len(s) > 1:
|
||||||
# Collect&sum prefixes from any order: _qee^s4 etc.
|
# Collect&sum prefixes from any order: _qee^s4 etc.
|
||||||
result = sum_dict(s)
|
result = sum_dict(s)
|
||||||
return Pitch(**result)
|
return Pitch(**result)
|
||||||
@ -52,7 +52,7 @@ class ZiffersTransformer(Transformer):
|
|||||||
return {"oct": int(value), "text": s[0].value}
|
return {"oct": int(value), "text": s[0].value}
|
||||||
|
|
||||||
def octave(self, s):
|
def octave(self, s):
|
||||||
value = sum([1 if char=='^' else -1 for char in s[0].value])
|
value = sum([1 if char == "^" else -1 for char in s[0].value])
|
||||||
return {"oct": value, "text": s[0].value}
|
return {"oct": value, "text": s[0].value}
|
||||||
|
|
||||||
def chord(self, s):
|
def chord(self, s):
|
||||||
@ -67,7 +67,7 @@ class ZiffersTransformer(Transformer):
|
|||||||
durs = 0.0
|
durs = 0.0
|
||||||
for (dchar, dots) in s:
|
for (dchar, dots) in s:
|
||||||
val = default_durs[dchar]
|
val = default_durs[dchar]
|
||||||
if(dots>0):
|
if dots > 0:
|
||||||
val = val * (2.0 - (1.0 / (2 * dots)))
|
val = val * (2.0 - (1.0 / (2 * dots)))
|
||||||
chars = chars + (dchar + "." * dots)
|
chars = chars + (dchar + "." * dots)
|
||||||
durs = durs + val
|
durs = durs + val
|
||||||
@ -100,7 +100,7 @@ class ZiffersTransformer(Transformer):
|
|||||||
key = s[0]
|
key = s[0]
|
||||||
val = default_durs[key]
|
val = default_durs[key]
|
||||||
dots = len(s) - 1
|
dots = len(s) - 1
|
||||||
if(dots>0):
|
if dots > 0:
|
||||||
val = val * (2.0 - (1.0 / (2 * dots)))
|
val = val * (2.0 - (1.0 / (2 * dots)))
|
||||||
return [key + "." * dots, val]
|
return [key + "." * dots, val]
|
||||||
|
|
||||||
@ -120,7 +120,9 @@ class ZiffersTransformer(Transformer):
|
|||||||
|
|
||||||
def subdivision(self, items):
|
def subdivision(self, items):
|
||||||
values = flatten(items[0])
|
values = flatten(items[0])
|
||||||
return Subdivision(values=values,text="["+"".join([val.text for val in values])+"]")
|
return Subdivision(
|
||||||
|
values=values, text="[" + "".join([val.text for val in values]) + "]"
|
||||||
|
)
|
||||||
|
|
||||||
def subitems(self, s):
|
def subitems(self, s):
|
||||||
return s
|
return s
|
||||||
@ -155,16 +157,28 @@ class ZiffersTransformer(Transformer):
|
|||||||
if len(items) > 2:
|
if len(items) > 2:
|
||||||
prefixes = sum_dict(items[0:-2]) # If there are prefixes
|
prefixes = sum_dict(items[0:-2]) # If there are prefixes
|
||||||
if items[-1] != None:
|
if items[-1] != None:
|
||||||
seq = RepeatedListSequence(values=items[-2],repeats=items[-1],wrap_end=":"+items[-1].text+")")
|
seq = RepeatedListSequence(
|
||||||
|
values=items[-2],
|
||||||
|
repeats=items[-1],
|
||||||
|
wrap_end=":" + items[-1].text + ")",
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
seq = RepeatedListSequence(values=items[-2],repeats=Integer(text='1', value=1))
|
seq = RepeatedListSequence(
|
||||||
|
values=items[-2], repeats=Integer(text="1", value=1)
|
||||||
|
)
|
||||||
seq.update_values(prefixes)
|
seq.update_values(prefixes)
|
||||||
return seq
|
return seq
|
||||||
else:
|
else:
|
||||||
if items[-1] != None:
|
if items[-1] != None:
|
||||||
seq = RepeatedListSequence(values=items[-2],repeats=items[-1],wrap_end=":"+items[-1].text+")")
|
seq = RepeatedListSequence(
|
||||||
|
values=items[-2],
|
||||||
|
repeats=items[-1],
|
||||||
|
wrap_end=":" + items[-1].text + ")",
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
seq = RepeatedListSequence(values=items[-2],repeats=Integer(text='1', value=1))
|
seq = RepeatedListSequence(
|
||||||
|
values=items[-2], repeats=Integer(text="1", value=1)
|
||||||
|
)
|
||||||
return seq
|
return seq
|
||||||
|
|
||||||
def SIGNED_NUMBER(self, s):
|
def SIGNED_NUMBER(self, s):
|
||||||
@ -180,7 +194,11 @@ class ZiffersTransformer(Transformer):
|
|||||||
def lisp_operation(self, s):
|
def lisp_operation(self, s):
|
||||||
op = s[0]
|
op = s[0]
|
||||||
values = s[1:]
|
values = s[1:]
|
||||||
return Operation(operator=op,values=values,text="(+"+"".join([v.text for v in values])+")")
|
return Operation(
|
||||||
|
operator=op,
|
||||||
|
values=values,
|
||||||
|
text="(+" + "".join([v.text for v in values]) + ")",
|
||||||
|
)
|
||||||
|
|
||||||
def operator(self, s):
|
def operator(self, s):
|
||||||
val = s[0].value
|
val = s[0].value
|
||||||
@ -209,6 +227,8 @@ class ZiffersTransformer(Transformer):
|
|||||||
|
|
||||||
def repeat(self, s):
|
def repeat(self, s):
|
||||||
if s[-1] != None:
|
if s[-1] != None:
|
||||||
return RepeatedSequence(values=s[0],repeats=s[-1],wrap_end=":"+s[-1].text+"]")
|
return RepeatedSequence(
|
||||||
|
values=s[0], repeats=s[-1], wrap_end=":" + s[-1].text + "]"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return RepeatedSequence(values=s[0],repeats=Integer(value=1,text='1'))
|
return RepeatedSequence(values=s[0], repeats=Integer(value=1, text="1"))
|
||||||
|
|||||||
@ -6,7 +6,14 @@ from lark import Lark
|
|||||||
grammar_path = Path(__file__).parent
|
grammar_path = Path(__file__).parent
|
||||||
grammar = grammar_path / "ziffers.lark"
|
grammar = grammar_path / "ziffers.lark"
|
||||||
|
|
||||||
ziffers_parser = Lark.open(grammar, rel_to=__file__, start='root', parser='lalr', transformer=ZiffersTransformer())
|
ziffers_parser = Lark.open(
|
||||||
|
grammar,
|
||||||
|
rel_to=__file__,
|
||||||
|
start="root",
|
||||||
|
parser="lalr",
|
||||||
|
transformer=ZiffersTransformer(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_expression(expr):
|
def parse_expression(expr):
|
||||||
return ziffers_parser.parse(expr)
|
return ziffers_parser.parse(expr)
|
||||||
@ -1495,15 +1495,7 @@ SCALES = {
|
|||||||
"Chromatic": 111111111111,
|
"Chromatic": 111111111111,
|
||||||
}
|
}
|
||||||
|
|
||||||
note_to_interval = {
|
note_to_interval = {"C": 0, "D": 2, "E": 4, "F": 5, "G": 7, "A": 9, "B": 11}
|
||||||
"C": 0,
|
|
||||||
"D": 2,
|
|
||||||
"E": 4,
|
|
||||||
"F": 5,
|
|
||||||
"G": 7,
|
|
||||||
"A": 9,
|
|
||||||
"B": 11
|
|
||||||
}
|
|
||||||
|
|
||||||
modifiers = {
|
modifiers = {
|
||||||
"#": 1,
|
"#": 1,
|
||||||
@ -1511,8 +1503,9 @@ modifiers = {
|
|||||||
"s": 1,
|
"s": 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def note_to_midi(name: str) -> int:
|
def note_to_midi(name: str) -> int:
|
||||||
''' Parse note name to midi '''
|
"""Parse note name to midi"""
|
||||||
items = re.match(r"^([a-gA-G])([#bs])?([1-9])?$", name)
|
items = re.match(r"^([a-gA-G])([#bs])?([1-9])?$", name)
|
||||||
if items == None:
|
if items == None:
|
||||||
return 60
|
return 60
|
||||||
@ -1522,11 +1515,10 @@ def note_to_midi(name: str) -> int:
|
|||||||
interval = note_to_interval[values[0].capitalize()]
|
interval = note_to_interval[values[0].capitalize()]
|
||||||
return 12 + octave * 12 + interval + modifier
|
return 12 + octave * 12 + interval + modifier
|
||||||
|
|
||||||
|
|
||||||
def get_scale(name: str) -> list:
|
def get_scale(name: str) -> list:
|
||||||
"""Get a scale from the global scale list"""
|
"""Get a scale from the global scale list"""
|
||||||
scale = SCALES.get(
|
scale = SCALES.get(name.lower().capitalize(), SCALES["Chromatic"])
|
||||||
name.lower().capitalize(),
|
|
||||||
SCALES["Chromatic"])
|
|
||||||
return list(map(int, str(scale)))
|
return list(map(int, str(scale)))
|
||||||
|
|
||||||
|
|
||||||
@ -1536,17 +1528,17 @@ def note_from_pc(
|
|||||||
intervals: str | list[int | float],
|
intervals: str | list[int | float],
|
||||||
cents: bool = False,
|
cents: bool = False,
|
||||||
octave: int = 0,
|
octave: int = 0,
|
||||||
modifier: int = 0) -> int:
|
modifier: int = 0,
|
||||||
|
) -> int:
|
||||||
"""Resolve a pitch class into a note from a scale"""
|
"""Resolve a pitch class into a note from a scale"""
|
||||||
|
|
||||||
# Initialization
|
# Initialization
|
||||||
root = note_to_midi(root) if isinstance(root, str) else root
|
root = note_to_midi(root) if isinstance(root, str) else root
|
||||||
intervals = get_scale(intervals) if isinstance(
|
intervals = get_scale(intervals) if isinstance(intervals, str) else intervals
|
||||||
intervals, str) else intervals
|
|
||||||
intervals = list(map(lambda x: x / 100), intervals) if cents else intervals
|
intervals = list(map(lambda x: x / 100), intervals) if cents else intervals
|
||||||
|
|
||||||
# Computing the result
|
# Computing the result
|
||||||
interval_sum = sum(intervals[0 : pitch_class % len(intervals)])
|
interval_sum = sum(intervals[0 : pitch_class % len(intervals)])
|
||||||
|
|
||||||
note = (root + interval_sum if pitch_class >= 0 else root - interval_sum)
|
note = root + interval_sum if pitch_class >= 0 else root - interval_sum
|
||||||
return note + octave * sum(intervals) + modifier
|
return note + octave * sum(intervals) + modifier
|
||||||
|
|||||||
Reference in New Issue
Block a user