Source code for idtap.classes.section

from __future__ import annotations

from typing import List, Optional

from .phrase import Phrase
from .pitch import Pitch
from .trajectory import Trajectory
from .piece import init_sec_categorization, SecCatType
from .raga import TuningType


[docs] class Section:
[docs] def __init__(self, options: Optional[dict] = None) -> None: opts = options or {} # Parameter validation self._validate_parameters(opts) self.phrases: List[Phrase] = opts.get('phrases', []) categorization = opts.get('categorization') if categorization is not None: self.categorization: SecCatType = categorization else: self.categorization = init_sec_categorization() ad_hoc = opts.get('ad_hoc_categorization') if ad_hoc is not None: self.ad_hoc_categorization: List[str] = ad_hoc else: self.ad_hoc_categorization = []
def _validate_parameters(self, opts: dict) -> None: """Validate constructor parameters and provide helpful error messages.""" if not opts: return # Define allowed parameter names allowed_keys = {'phrases', 'categorization', 'ad_hoc_categorization'} provided_keys = set(opts.keys()) invalid_keys = provided_keys - allowed_keys # Check for invalid parameter names if invalid_keys: error_messages = [] for key in invalid_keys: if key == 'phrase_list': error_messages.append(f"Parameter '{key}' not supported. Did you mean 'phrases'?") else: error_messages.append(f"Invalid parameter: '{key}'") error_msg = "; ".join(error_messages) error_msg += f". Allowed parameters: {sorted(allowed_keys)}" raise ValueError(error_msg) # Validate parameter types if 'phrases' in opts: if not isinstance(opts['phrases'], list): raise TypeError(f"Parameter 'phrases' must be a list, got {type(opts['phrases']).__name__}") if 'categorization' in opts and opts['categorization'] is not None: if not isinstance(opts['categorization'], dict): raise TypeError(f"Parameter 'categorization' must be a dict, got {type(opts['categorization']).__name__}") if 'ad_hoc_categorization' in opts and opts['ad_hoc_categorization'] is not None: if not isinstance(opts['ad_hoc_categorization'], list): raise TypeError(f"Parameter 'ad_hoc_categorization' must be a list, got {type(opts['ad_hoc_categorization']).__name__}") if not all(isinstance(item, str) for item in opts['ad_hoc_categorization']): raise TypeError("All items in 'ad_hoc_categorization' must be strings") # ------------------------------------------------------------------
[docs] def all_pitches(self, repetition: bool = True) -> List[Pitch]: pitches: List[Pitch] = [] for phrase in self.phrases: pitches.extend(phrase.all_pitches(True)) if not repetition: out: List[Pitch] = [] for p in pitches: if not out: out.append(p) else: prev = out[-1] if not ( p.swara == prev.swara and p.oct == prev.oct and p.raised == prev.raised ): out.append(p) return out return pitches
# ------------------------------------------------------------------ @property def trajectories(self) -> List[Trajectory]: trajs: List[Trajectory] = [] for phrase in self.phrases: trajs.extend(phrase.trajectories) return trajs
et_tuning: TuningType = { 'sa': 2 ** (0 / 12), 're': { 'lowered': 2 ** (1 / 12), 'raised': 2 ** (2 / 12), }, 'ga': { 'lowered': 2 ** (3 / 12), 'raised': 2 ** (4 / 12), }, 'ma': { 'lowered': 2 ** (5 / 12), 'raised': 2 ** (6 / 12), }, 'pa': 2 ** (7 / 12), 'dha': { 'lowered': 2 ** (8 / 12), 'raised': 2 ** (9 / 12), }, 'ni': { 'lowered': 2 ** (10 / 12), 'raised': 2 ** (11 / 12), }, }