Transpose

This commit is contained in:
khodowany
2026-09-16 21:33:34 -05:00
parent c6adff8b3c
commit 01c6d4e786
14 changed files with 526 additions and 50 deletions
+4 -13
View File
@@ -1,26 +1,18 @@
from __future__ import annotations
from itertools import product
from typing import NamedTuple
from generator.transposition import mod12
from generator import harmony
from generator.model import NATURAL, alteration
STANDARD_TUNING = ((6, 40), (5, 45), (4, 50), (3, 55), (2, 59), (1, 64))
WRITTEN_GUITAR_OCTAVE_OFFSET = 12
class TabPosition(NamedTuple):
string_number: int
fret: int
midi: int
from generator.guitar import MAX_FRET, MIN_FRET, STANDARD_TUNING, TabPosition, WRITTEN_GUITAR_OCTAVE_OFFSET, within_fret_span
def chord_pitch_classes(symbol: str | dict) -> tuple[int, ...]:
return harmony.pitch_classes(harmony.parse_symbol(symbol) if isinstance(symbol, str) else symbol)
def positions_for_string(string_number: int, open_midi: int, pitch_classes: tuple[int, ...]) -> list[TabPosition]:
result: list[TabPosition] = []
for fret in range(0, 13):
for fret in range(MIN_FRET, MAX_FRET + 1):
midi = open_midi + fret
if mod12(midi) in pitch_classes:
result.append(TabPosition(string_number, fret, midi))
@@ -63,14 +55,13 @@ def find_playable_voicing(symbol: str | dict) -> list[TabPosition]:
best: tuple[TabPosition, ...] | None = None
best_score = 10**9
for string_set in string_sets:
options = [by_string[string_number][:8] for string_number in string_set]
options = [by_string[string_number] for string_number in string_set]
if any(not item for item in options):
continue
for candidate in product(*options):
if bass_pc is not None and min(candidate, key=lambda p: p.midi).midi % 12 != bass_pc:
continue
fretted = [position.fret for position in candidate if position.fret > 0]
if fretted and max(fretted) - min(fretted) > 4:
if not within_fret_span(candidate):
continue
covered = {mod12(position.midi) for position in candidate}
if not set(pitch_classes).issubset(covered):