Spaces:
Sleeping
Sleeping
| """Common symbols and musical constants for generation engines.""" | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| PITCH_CLASS = { | |
| "C": 0, | |
| "B#": 0, | |
| "C#": 1, | |
| "Db": 1, | |
| "D": 2, | |
| "D#": 3, | |
| "Eb": 3, | |
| "E": 4, | |
| "Fb": 4, | |
| "E#": 5, | |
| "F": 5, | |
| "F#": 6, | |
| "Gb": 6, | |
| "G": 7, | |
| "G#": 8, | |
| "Ab": 8, | |
| "A": 9, | |
| "A#": 10, | |
| "Bb": 10, | |
| "B": 11, | |
| "Cb": 11, | |
| } | |
| PC_TO_SHARP_NAME = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | |
| COMMON_DURATION_BEATS = { | |
| "64th": 0.0625, | |
| "32nd": 0.125, | |
| "16th triplet": 1 / 6, | |
| "16th": 0.25, | |
| "eighth triplet": 1 / 3, | |
| "dotted 16th": 0.375, | |
| "eighth": 0.5, | |
| "quarter triplet": 2 / 3, | |
| "dotted eighth": 0.75, | |
| "quarter": 1.0, | |
| "dotted quarter": 1.5, | |
| "half": 2.0, | |
| "dotted half": 3.0, | |
| "whole": 4.0, | |
| } | |
| ABC_DURATION = { | |
| "64th": "/4", | |
| "32nd": "/2", | |
| "16th triplet": "/3", | |
| "16th": "", | |
| "eighth triplet": "4/3", | |
| "dotted 16th": "3/2", | |
| "eighth": "2", | |
| "quarter triplet": "8/3", | |
| "dotted eighth": "3", | |
| "quarter": "4", | |
| "dotted quarter": "6", | |
| "half": "8", | |
| "dotted half": "12", | |
| "whole": "16", | |
| } | |
| DEFAULT_DURATIONS = { | |
| "16th", | |
| "eighth", | |
| "quarter", | |
| "half", | |
| "dotted eighth", | |
| "dotted quarter", | |
| "eighth triplet", | |
| "16th triplet", | |
| } | |
| START_SYMBOL = "__THEME_GENERATION_START__" | |
| class Symbol: | |
| """A key-relative pitch class plus notated duration.""" | |
| rpc: int | |
| duration: str | |
| def __str__(self) -> str: | |
| return f"{self.rpc}:{self.duration}" | |
| def duration_options(min_duration: str, duration_grid: str, allow_triplets: bool) -> set[str]: | |
| if min_duration not in COMMON_DURATION_BEATS: | |
| labels = ", ".join(sorted(COMMON_DURATION_BEATS)) | |
| raise ValueError(f"Unknown duration {min_duration!r}; choose one of: {labels}") | |
| if duration_grid not in COMMON_DURATION_BEATS: | |
| labels = ", ".join(sorted(COMMON_DURATION_BEATS)) | |
| raise ValueError(f"Unknown duration grid {duration_grid!r}; choose one of: {labels}") | |
| min_beats = COMMON_DURATION_BEATS[min_duration] | |
| grid_beats = COMMON_DURATION_BEATS[duration_grid] | |
| allowed = set() | |
| for duration in DEFAULT_DURATIONS: | |
| duration_beats = COMMON_DURATION_BEATS[duration] | |
| on_grid = abs((duration_beats / grid_beats) - round(duration_beats / grid_beats)) < 1e-9 | |
| regular_triplet = allow_triplets and "triplet" in duration | |
| if duration_beats >= min_beats and (on_grid or regular_triplet): | |
| allowed.add(duration) | |
| return allowed | |
| def duration_units(duration: str) -> int: | |
| return round(COMMON_DURATION_BEATS[duration] * 12) | |
| def is_triplet_duration(duration: str) -> bool: | |
| return "triplet" in duration | |
| def parse_key_root(raw_key: str | None) -> int | None: | |
| if not raw_key: | |
| return None | |
| root = raw_key.split("%", 1)[0].strip().replace("m", "") | |
| return PITCH_CLASS.get(root) | |