Source code for musif.extract.features.scale.handler

import re
from statistics import mean

from typing import Dict, List, Union


from music21.note import Note


from musif.config import Configuration

from musif.extract.common import _filter_parts_data

from musif.extract.features.core.handler import DATA_KEY, DATA_NOTES

from musif.extract.features.prefix import get_part_feature, get_score_feature

from musif.musicxml.common import _get_degrees_and_accidentals

from .constants import *

from ...constants import DATA_PART_ABBREVIATION


[docs]def update_part_objects( score_data: dict, part_data: dict, cfg: Configuration, part_features: dict ): notes = part_data[DATA_NOTES] key = score_data[DATA_KEY] notes_per_degree = get_notes_per_degree(str(key), notes) all_degrees = sum(value for value in notes_per_degree.values()) for key, value in notes_per_degree.items(): part_features[DEGREE_COUNT.format(key=key)] = value part_features[DEGREE_PER.format(key=key)] = ( value / all_degrees if all_degrees != 0 else 0 )
[docs]def update_score_objects( score_data: dict, parts_data: List[dict], cfg: Configuration, parts_features: List[dict], score_features: dict, ): parts_data = _filter_parts_data(parts_data, cfg.parts_filter) if len(parts_data) == 0: return key = score_data[DATA_KEY] score_notes_per_degree = {} for part_data in parts_data: notes = part_data[DATA_NOTES] part_notes_per_degree = get_notes_per_degree(str(key), notes) for degree, notes in part_notes_per_degree.items(): if degree not in score_notes_per_degree: score_notes_per_degree[degree] = 0 score_notes_per_degree[degree] += notes all_score_degrees = sum(value for value in score_notes_per_degree.values()) for key, value in score_notes_per_degree.items(): score_features[get_score_feature(DEGREE_COUNT.format(key=key))] = value score_features[get_score_feature(DEGREE_PER.format(key=key))] = ( value / all_score_degrees if all_score_degrees != 0 else 0 ) for part_data, parts_features in zip(parts_data, parts_features): part = part_data[DATA_PART_ABBREVIATION] degree_count_pattern = DEGREE_COUNT.format(key=".+") degree_per_pattern = DEGREE_PER.format(key=".+") for feature_name in parts_features: if re.match(degree_count_pattern, feature_name) or re.match( degree_per_pattern, feature_name ): part_feature = get_part_feature(part, feature_name) if part_feature in score_features: score_features[part_feature] = mean( [score_features[part_feature], parts_features[feature_name]] ) else: score_features[part_feature] = parts_features[feature_name]
[docs]def get_notes_per_degree(key: str, notes: List[Note]) -> Dict[str, int]: notes_per_degree = { to_full_degree(degree, accidental): 0 for accidental in ["", "sharp", "flat"] for degree in [1, 2, 3, 4, 5, 6, 7] } all_degrees = 0 for degree, accidental in _get_degrees_and_accidentals(key, notes): if to_full_degree(degree, accidental) not in notes_per_degree: continue notes_per_degree[to_full_degree(degree, accidental)] += 1 all_degrees += 1 return notes_per_degree
[docs]def to_full_degree(degree: Union[int, str], accidental: str) -> str: return f"{ACCIDENTAL_ABBREVIATION[accidental]}{degree}"