Added pitch resolving to roman numeral chords
This commit is contained in:
@ -4,7 +4,7 @@ import itertools
|
||||
import operator
|
||||
import random
|
||||
from .defaults import DEFAULT_OPTIONS
|
||||
from .scale import note_from_pc
|
||||
from .scale import note_from_pc, midi_to_pitch_class
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -79,6 +79,7 @@ class Pitch(Event):
|
||||
|
||||
pitch_class: int = field(default=None)
|
||||
octave: int = field(default=None)
|
||||
modifier: int = field(default=None)
|
||||
note: int = field(default=None)
|
||||
|
||||
def set_note(self, note: int):
|
||||
@ -118,6 +119,16 @@ class RomanNumeral(Event):
|
||||
value: str = field(default=None)
|
||||
chord_type: str = field(default=None)
|
||||
notes: list[int] = field(default_factory=[])
|
||||
pitch_classes: list = None
|
||||
|
||||
def set_notes(self, chord_notes: list[int]):
|
||||
self.notes = chord_notes
|
||||
|
||||
def set_pitch_classes(self, pitches: list[tuple]):
|
||||
if self.pitch_classes == None:
|
||||
self.pitch_classes = []
|
||||
for pitch in pitches:
|
||||
self.pitch_classes.append(Pitch(**pitch))
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -212,7 +223,7 @@ class Ziffers(Sequence):
|
||||
# Update collected options & default options
|
||||
self.current.update_new(self.options)
|
||||
|
||||
# Resolve note from scale
|
||||
# Resolve note(s) from scale
|
||||
if set(("key", "scale")) <= self.options.keys():
|
||||
key = self.options["key"]
|
||||
scale = self.options["scale"]
|
||||
@ -223,6 +234,10 @@ class Ziffers(Sequence):
|
||||
pcs = self.current.pitch_classes
|
||||
notes = [pc.set_note(note_from_pc(key, pc.pitch_class, scale)) for pc in pcs]
|
||||
self.current.set_notes(notes)
|
||||
elif isinstance(self.current,RomanNumeral):
|
||||
pitch_classes = [midi_to_pitch_class(note, key, scale) for note in self.current.notes]
|
||||
self.current.set_pitch_classes(pitch_classes)
|
||||
|
||||
|
||||
self.loop_i += 1
|
||||
return self.current
|
||||
|
||||
@ -114,8 +114,9 @@ class ZiffersTransformer(Transformer):
|
||||
numeral = items[0].value
|
||||
if len(items)>1:
|
||||
name = items[1]
|
||||
notes = chord_from_roman_numeral(numeral,name)
|
||||
return RomanNumeral(text=numeral, value=parse_roman(numeral), chord_type=name, notes=notes)
|
||||
chord_notes = chord_from_roman_numeral(numeral,name)
|
||||
parsed_number = parse_roman(numeral)
|
||||
return RomanNumeral(text=numeral, value=parsed_number, chord_type=name, notes=chord_notes)
|
||||
return RomanNumeral(value=parse_roman(numeral), text=numeral, notes=chord_from_roman_numeral(numeral))
|
||||
|
||||
def chord_name(self,item):
|
||||
|
||||
@ -188,18 +188,6 @@ def midi_to_tpc(note: int, key: str | int):
|
||||
return (note * 7 + 26 - (11 + acc)) % 12 + (11 + acc)
|
||||
|
||||
|
||||
def midi_to_pitch_class(note: int) -> int:
|
||||
"""Return pitch class from midi
|
||||
|
||||
Args:
|
||||
note (int): Note in midi
|
||||
|
||||
Returns:
|
||||
int: Returns note % 12
|
||||
"""
|
||||
return note % 12
|
||||
|
||||
|
||||
def midi_to_octave(note: int) -> int:
|
||||
"""Return octave for the midi note
|
||||
|
||||
@ -212,7 +200,7 @@ def midi_to_octave(note: int) -> int:
|
||||
return 0 if note <= 0 else floor(note / 12)
|
||||
|
||||
|
||||
def midi_to_pc(note: int, key: str | int, scale: str) -> tuple:
|
||||
def midi_to_pitch_class(note: int, key: str | int, scale: str) -> dict:
|
||||
"""Return pitch class and octave from given midi note, key and scale
|
||||
|
||||
Args:
|
||||
@ -223,13 +211,14 @@ def midi_to_pc(note: int, key: str | int, scale: str) -> tuple:
|
||||
Returns:
|
||||
tuple: Returns tuple containing (pitch class as string, pitch class, octave, optional modifier)
|
||||
"""
|
||||
sharps = ["0", "#0", "1", "#1", "2", "3", "#3", "4", "#4", "5", "#5", "6"]
|
||||
flats = ["0", "b1", "1", "b2", "2", "3", "b4", "4", "b5", "5", "b6", "6"]
|
||||
tpc = midi_to_tpc(note, key)
|
||||
pitch_class = midi_to_pitch_class(note)
|
||||
pitch_class = note % 12
|
||||
octave = midi_to_octave(note) - 5
|
||||
if scale.upper() == "CHROMATIC":
|
||||
return (str(pitch_class), pitch_class, octave)
|
||||
|
||||
sharps = ["0", "#0", "1", "#1", "2", "3", "#3", "4", "#4", "5", "#5", "6"]
|
||||
flats = ["0", "b1", "1", "b2", "2", "3", "b4", "4", "b5", "5", "b6", "6"]
|
||||
tpc = midi_to_tpc(note, key)
|
||||
if tpc >= 6 and tpc <= 12 and len(flats[pitch_class]) == 2:
|
||||
npc = flats[pitch_class]
|
||||
elif tpc >= 20 and tpc <= 26 and len(sharps[pitch_class]) == 2:
|
||||
@ -238,12 +227,19 @@ def midi_to_pc(note: int, key: str | int, scale: str) -> tuple:
|
||||
npc = sharps[pitch_class]
|
||||
|
||||
if len(npc) > 1:
|
||||
return (npc, int(npc[1]), octave, 1 if (npc[0] == "#") else -1)
|
||||
return {
|
||||
"text": npc,
|
||||
"pitch_class": int(npc[1]),
|
||||
"octave": octave,
|
||||
"modifier": 1 if (npc[0] == "#") else -1,
|
||||
}
|
||||
|
||||
return (npc, int(npc), octave)
|
||||
return {"text": npc, "pitch_class": int(npc), "octave": octave}
|
||||
|
||||
|
||||
def chord_from_roman_numeral(roman: str, name: str = "major", num_octaves: int = 1) -> list[int]:
|
||||
def chord_from_roman_numeral(
|
||||
roman: str, name: str = "major", num_octaves: int = 1
|
||||
) -> list[int]:
|
||||
"""Generates chord from given roman numeral and chord name
|
||||
|
||||
Args:
|
||||
@ -256,7 +252,7 @@ def chord_from_roman_numeral(roman: str, name: str = "major", num_octaves: int =
|
||||
"""
|
||||
root = parse_roman(roman) - 1
|
||||
tonic = (DEFAULT_OCTAVE * 12) + root + 12
|
||||
intervals = CHORDS.get(name,CHORDS["major"])
|
||||
intervals = CHORDS.get(name, CHORDS["major"])
|
||||
notes = []
|
||||
for cur_oct in range(num_octaves):
|
||||
for iterval in intervals:
|
||||
|
||||
Reference in New Issue
Block a user