""" AccousticBrainz Adapter """ import numpy as np from typing import List META_FEATURES = [ 'isrc', 'album', 'originaldate', 'genre', 'artist', 'title', 'musicbrainz album release country', 'bit_rate', 'length' 'replay_gain', 'replaygain_track_gain', 'script' ] LOW_LEVEL_FEATURES = [ 'hfc', 'gfcc', 'mfcc', 'erbbands', 'melbands', 'barkbands', 'dissonance', 'average_loudness', 'zerocrossingrate', 'silence_rate_60dB', 'silence_rate_20dB', 'silence_rate_30dB', 'pitch_salience' ] TONAL_FEATURES = [ 'hpcp', 'thpcp', 'key_key', 'key_scale', 'key_strength', 'chords_key', 'chords_strength', 'tuning_frequency', 'chords_number_rate', 'chords_changes_rate', 'tuning_diatonic_strength', 'chords_histogram' ] RYTHMIC_FEATURES = [ 'bpm', 'onset_rate', 'beats_count', 'beats_loudness', 'danceability', 'beats_loudness', 'beats_position', 'beats_loudness_band_ratio' ] class AccousticBrainzAdapter: def __init__(self, meat_features=META_FEATURES, low_level_features=LOW_LEVEL_FEATURES, tonal_features=TONAL_FEATURES, rythmic_features=RYTHMIC_FEATURES): # init features self.meta_features = meat_features self.low_level_features = low_level_features self.tonal_features = tonal_features self.rythmic_features = rythmic_features def transform(self, track_features: dict) -> dict: """ Reads from raw accoustic brainz file and returns transformed data in a dictionary Params: track_features (dict): raw metadata features Returns: dict: transformed features """ # load and transform features and put into final dictionary final_features = { **self.get_meta_features(track_features), **self.get_lowlevel_features(track_features), **self.get_rythmic_features(track_features), **self.get_tonal_features(track_features) } return final_features def get_meta_features(self, track_features:dict, meta_features=META_FEATURES): """ Extracts meta features Params: - meat_features (list, optional): list of meta features to extract. Defaults to []. """ track_meta = track_features['metadata'] def _flatten_nested_values(val): if isinstance(val, list): return val[0] else: return val tags = { k: _flatten_nested_values(v) for k, v in track_meta['tags'].items() if k in meta_features } audio_properties = { k: v for k, v in track_meta['audio_properties'].items() if k in meta_features } return {**tags, **audio_properties} @staticmethod def _fetch_features(selected_features: List[str], raw_feature_dict: dict) -> dict: """ Selects features from raw feature dictionary from accoustic brainz Args: selected_features (List[str]): features to select raw_feature_dict (dict): raw fictionary fictionary Returns: dict: Features selected """ features = {} for feat_i in selected_features: # copy features if isinstance(raw_feature_dict[feat_i], dict): if 'mean' in raw_feature_dict[feat_i].keys(): features[f'{feat_i}_mean'] = raw_feature_dict[feat_i]['mean'] else: features[feat_i] = raw_feature_dict[feat_i] return features def get_lowlevel_features(self, track_features:dict, lowlevel_features:List[str] = LOW_LEVEL_FEATURES) -> dict: """ Fetches and returns low level features from accoustic brainz Params: track_features (dict): Raw json dictionary from accoustic brainz lowlevel_features (List[str], optional): Low level features to fetch. Defaults to LOW_LEVEL_FEATURES. Returns: dict: Selected low level features """ lowlevel_feats_dict = track_features['lowlevel'] features = self._fetch_features(selected_features=lowlevel_features, raw_feature_dict=lowlevel_feats_dict) return features def get_tonal_features(self, track_features:dict, tonal_features:List[str] = TONAL_FEATURES) -> dict: """ Fetches and returns tonal features from accoustic brainz Params: track_features (dict): Raw json dictionary from accoustic brainz tonal_features (List[str], optional): Tonal features to select. Defaults to TONAL_FEATURES. Returns: dict: Selected tonal features """ tonal_feats_dict = track_features['tonal'] features = self._fetch_features(selected_features=tonal_features, raw_feature_dict=tonal_feats_dict) return features def get_rythmic_features(self, track_features:dict, rythmic_features:List[str]=RYTHMIC_FEATURES) -> dict: """ Params: track_features (dict): Raw json dictionary from accoustic brainz rythmic_features (List[str], optional): Features to select. Defaults to RYTHMIC_FEATURES. Returns: dict: Selected rythmic features """ rythmic_feats_dict = track_features['rhythm'] features = self._fetch_features(selected_features=rythmic_features, raw_feature_dict=rythmic_feats_dict) return features