Author: Nikolay Lysenko """ import itertools import math import random from dataclasses import dataclass from typing import Any, Optional from sinethesizer.utils.music_theory import get_note_to_position_mapping from .music_theory import (N_SEMITONES_PER_OCTAVE, TONE_ROW_LEN, get_smallest_intervals_between_pitch_classes, invert_tone_row, revert_tone_row, validate_tone_row) NOTE_TO_POSITION_MAPPING = get_note_to_position_mapping() SMALLEST_INTERVALS_MAPPING = get_smallest_intervals_between_pitch_classes() SUPPORTED_DURATIONS = [0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0] DURATION_SPLITS = { 6.0: [[4.0, 2.0], [3.0, 3.0]], 4.0: [[2.0, 2.0], [3.0, 1.0]], 3.0: [[2.0, 1.0], [1.5, 1.5]], 2.0: [[1.0, 1.0], [1.5, 0.5]], 1.5: [[1.0, 0.5], [0.75, 0.75]], 1.0: [[0.5, 0.5], [0.75, 0.25]], 0.75: [[0.5, 0.25]], 0.5: [[0.25, 0.25]], 0.25: [[0.25]], }
""" Help to work with notions from music theory. Author: Nikolay Lysenko """ from typing import List, NamedTuple from sinethesizer.utils.music_theory import get_note_to_position_mapping NOTE_TO_POSITION = get_note_to_position_mapping() TONIC_TRIAD_DEGREES = (1, 3, 5) class ScaleElement(NamedTuple): """A pitch from a diatonic scale.""" note: str position_in_semitones: int position_in_degrees: int degree: int is_from_tonic_triad: bool class Scale: """A diatonic scale.""" def __init__(self, tonic: str, scale_type: str): """ Initialize an instance. :param tonic: