diff --git a/.gitignore b/.gitignore index d429ae9..26209e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ .DS_Store -chopin_cleaned/ -mscx_files/ +*.pyc \ No newline at end of file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 09867f5..0000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "musicxml_parser"] - path = musicxml_parser - url = https://github.com/jdasam/musicxml_parser.git -[submodule "midi_utils"] - path = midi_utils - url = https://github.com/mac-marg-pianist/midi_utils.git diff --git a/binary_index.py b/binary_index.py index f13d194..feded7f 100644 --- a/binary_index.py +++ b/binary_index.py @@ -1,29 +1,3 @@ -def binary_index(alist, item): - first = 0 - last = len(alist)-1 - midpoint = 0 - - if(item< alist[first]): - return 0 - - while first item: - return midpoint - else: first = midpoint +1 - if first == last and alist[last] > item: - return midpoint - elif currentElement > item: - last = midpoint -1 - else: - if midpoint +1 ==len(alist): - return midpoint - while alist[midpoint+1] == item: - midpoint += 1 - if midpoint + 1 == len(alist): - return midpoint - return midpoint - return last \ No newline at end of file +''' +move binary_index to utils.py +''' \ No newline at end of file diff --git a/data_class.py b/data_class.py new file mode 100644 index 0000000..213b1fb --- /dev/null +++ b/data_class.py @@ -0,0 +1,526 @@ +''' +split data classes to an individual file +''' +import os +import traceback +import pickle +import pandas +import math +import ntpath +import shutil +import subprocess +from pathlib import Path +import warnings +import copy +from abc import abstractmethod +from tqdm import tqdm +import _pickle as cPickle + +from musicxml_parser import MusicXMLDocument +from midi_utils import midi_utils +import score_as_graph as score_graph, xml_midi_matching as matching +import xml_utils +import feature_extraction + +align_dir = '/home/ilcobo2/projects/pyScoreParser/AlignmentTool_v190813' + +DEFAULT_SCORE_FEATURES = ['midi_pitch', 'duration', 'beat_importance', 'measure_length', 'qpm_primo', + 'following_rest', 'distance_from_abs_dynamic', 'distance_from_recent_tempo', + 'beat_position', 'xml_position', 'grace_order', 'preceded_by_grace_note', + 'followed_by_fermata_rest', 'pitch', 'tempo', 'dynamic', 'time_sig_vec', + 'slur_beam_vec', 'composer_vec', 'notation', 'tempo_primo', 'note_location'] +DEFAULT_PERFORM_FEATURES = ['beat_tempo', 'velocity', 'onset_deviation', 'articulation', 'pedal_refresh_time', + 'pedal_cut_time', 'pedal_at_start', 'pedal_at_end', 'soft_pedal', + 'pedal_refresh', 'pedal_cut', 'qpm_primo', 'align_matched', 'articulation_loss_weight', + 'beat_dynamics', 'measure_tempo', 'measure_dynamics'] + + +# total data class +class DataSet: + def __init__(self, path, save=False): + self.path = path + + self.pieces = [] + self.scores = [] + self.score_midis = [] + self.perform_midis = [] + self.composers = [] + + self.performances = [] + + self.performs_by_tag = {} + self.flattened_performs_by_tag = [] + + self.num_pieces = 0 + self.num_performances = 0 + self.num_score_notes = 0 + self.num_performance_notes = 0 + + # self.default_perforddmances = self.performances + # TGK: what for? + + # self._load_all_scores() + scores, score_midis, perform_midis, composers = self.load_data() + self.scores = scores + self.score_midis = score_midis + self.perform_midis = perform_midis + self.composers = composers + self.load_all_piece(scores, perform_midis, score_midis, composers, save=save) + + @classmethod + @abstractmethod + def load_data(self): + '''return scores, score_midis, performances, composers''' + raise NotImplementedError + + def load_all_piece(self, scores, perform_midis, score_midis, composers, save): + for n in tqdm(range(len(scores))): + try: + piece = PieceData(scores[n], perform_midis[n], score_midis[n], composers[n], save=save) + self.pieces.append(piece) + for perf in piece.performances: + self.performances.append(perf) + except Exception as ex: + # TODO: TGK: this is ambiguous. Can we specify which file + # (score? performance? matching?) and in which function the error occur? + traceback.print_tb(ex.__traceback__) + print(f'Error while processing {scores[n]}. Error type :{ex}') + self.num_performances = len(self.performances) + + ''' + # TGK : same as extract_selected_features(DEFAULT_SCORE_FEATURES) ... + def extract_all_features(self): + score_extractor = feature_extraction.ScoreExtractor(DEFAULT_SCORE_FEATURES) + perform_extractor = feature_extraction.PerformExtractor(DEFAULT_PERFORM_FEATURES) + for piece in self.pieces: + piece.score_features = score_extractor.extract_score_features(piece) + for perform in piece.performances: + perform.perform_features = perform_extractor.extract_perform_features(piece, perform) + ''' + + def _sort_performances(self): + # TODO: move to EmotionDataset + self.performances.sort(key=lambda x:x.midi_path) + for tag in self.performs_by_tag: + self.performs_by_tag[tag].sort(key=lambda x:x.midi_path) + + flattened_performs_by_tag = [] + for tag in self.performs_by_tag: + for perform in self.performs_by_tag[tag]: + flattened_performs_by_tag.append(perform) + # self.perform_name_by_tag.append(perform.midi_path) + self.flattened_performs_by_tag = flattened_performs_by_tag + + def save_dataset(self, filename='data_set.dat'): + with open(filename, "wb") as f: + pickle.dump(self, f, protocol=2) + + def save_features_as_csv(self, features, feature_names, path='features.csv'): + feature_type_name = ['MIDI Path'] + feature_names + feature_with_name = [feature_type_name] + perform_names = [x.midi_path for x in self.performances] + for i,feature_by_perf in enumerate(features): + feature_by_perf = [perform_names[i]] + feature_by_perf + feature_with_name.append(feature_by_perf) + dataframe = pandas.DataFrame(feature_with_name) + dataframe.to_csv(path) + + def save_features_by_features_as_csv(self, feature_data, list_of_features, path='measure.csv'): + for feature, feature_name in zip(feature_data, list_of_features): + save_name = feature_name + '_' + path + self.save_features_as_csv(feature, [feature_name], save_name) + + def features_to_list(self, list_of_feat): + feature_data = [[] for i in range(len(list_of_feat))] + for perf in self.performances: + for i, feature_type in enumerate(list_of_feat): + feature_data[i].append(perf.perform_features[feature_type]) + return feature_data + + def get_average_by_perform(self, feature_data): + # axis 0: feature, axis 1: performance, axis 2: note + average_data = [[] for i in range(len(feature_data[0]))] + for feature in feature_data: + for i, perf in enumerate(feature): + valid_list = [x for x in perf if x is not None] + avg = sum(valid_list) / len(valid_list) + average_data[i].append(avg) + + return average_data + + # def list_features_to_measure(self, feature_data): + # # axis 0: performance, axis 1: feature, axis 2: note + # for data_by_perf in feature_data: + + def get_average_feature_by_measure(self, list_of_features): + measure_average_features = [[] for i in range(len(list_of_features))] + for p_index, perf in enumerate(self.performances): + features_in_performance = [ [] for i in range(len(list_of_features))] + features_in_previous_measure = [ [] for i in range(len(list_of_features))] + previous_measure = 0 + for i, pair in enumerate(perf.pairs): + if pair == []: + continue + if pair['xml'].measure_number != previous_measure: + previous_measure = pair['xml'].measure_number + if features_in_previous_measure[0] != []: + for j, data_of_selected_features in enumerate(features_in_previous_measure): + if len(data_of_selected_features) > 0: + average_of_selected_feature = sum(data_of_selected_features) / len(data_of_selected_features) + else: + average_of_selected_feature = 0 + features_in_performance[j].append(average_of_selected_feature) + features_in_previous_measure = [[] for i in range(len(list_of_features))] + for j, target_feature in enumerate(list_of_features): + feature_value = perf.perform_features[target_feature][i] + if feature_value is not None: + features_in_previous_measure[j].append(feature_value) + for j, data_of_selected_features in enumerate(features_in_previous_measure): + if len(data_of_selected_features) > 0: + average_of_selected_feature = sum(data_of_selected_features) / len(data_of_selected_features) + else: + average_of_selected_feature = 0 + features_in_performance[j].append(average_of_selected_feature) + for j, perform_data_of_feature in enumerate(measure_average_features): + perform_data_of_feature.append(features_in_performance[j]) + return measure_average_features + + def _divide_by_tag(self, list_of_tag): + # TODO: move to EmotionDataset + # example of list_of_tag = ['professional', 'amateur'] + for tag in list_of_tag: + self.performs_by_tag[tag] = [] + for piece in self.pieces: + for perform in piece.performances: + for tag in list_of_tag: + if tag in perform.midi_path: + self.performs_by_tag[tag].append(perform) + break + + def __str__(self): + return str(self.__dict__) + + + +# score data class +class PieceData: + def __init__(self, xml_path, perform_lists, score_midi_path=None, composer=None, save=False): + if score_midi_path == None: + score_midi_path = os.path.dirname(xml_path) + '/' + Path(xml_path).stem + '_score.mid' + self.meta = PieceMeta(xml_path, perform_lists=perform_lists, score_midi_path=score_midi_path, composer=composer) + self.performances = [] + + score_dat_path = os.path.dirname(xml_path) + '/score.dat' + + if save: + self.score = ScoreData(xml_path, score_midi_path) + with open(score_dat_path , 'wb') as f: + pickle.dump(self.score, f, protocol=2) + else: + if Path(score_dat_path).exists(): + with open(score_dat_path, 'rb') as f: + u = cPickle.Unpickler(f) + self.score = u.load() + else: + print(f'not exist {score_dat_path}. make one') + self.score = ScoreData(xml_path, score_midi_path) + with open(score_dat_path , 'wb') as f: + pickle.dump(self.score, f, protocol=2) + + # ScoreData alias + self.xml_obj = self.score.xml_obj + self.xml_notes = self.score.xml_notes + self.num_notes = self.score.num_notes + self.notes_graph = self.score.notes_graph + self.score_midi_notes = self.score.score_midi_notes + self.score_match_list = self.score.score_match_list + self.score_pairs = self.score.score_pairs + self.measure_positions = self.score.measure_positions + self.beat_positions = self.score.beat_positions + self.section_positions = self.score.section_positions + + # TODO: move to ScoreData + self.score_features = {} + self.meta._check_perf_align() + + + for perform in perform_lists: + perform_dat_path = Path(perform).parent / Path(perform).name.replace('.mid', '.dat') + if not save: + if not perform_dat_path.exists(): + print(f'not exist {perform_dat_path}.') + with open(perform_dat_path, 'rb') as f: + u = cPickle.Unpickler(f) + perform_data = u.load() + self.performances.append(perform_data) + else: + try: + perform_data = PerformData(perform, self.meta) + self._align_perform_with_score(perform_data) + self.performances.append(perform_data) + except: + perform_data = None + print(f'Cannot align {perform}') + self.performances.append(None) + if save: + if perform_data is not None: + with open(perform_dat_path, 'wb') as f: + pickle.dump(perform_data, f, protocol=2) + + def extract_perform_features(self, target_features): + perform_extractor = feature_extraction.PerformExtractor(target_features) + for perform in self.performances: + print('Performance:', perform.midi_path) + for feature_name in target_features: + perform.perform_features[feature_name] = getattr(perform_extractor, 'get_'+ feature_name)(self, perform) + + def extract_score_features(self, target_features): + score_extractor = feature_extraction.ScoreExtractor(target_features) + self.score_features = score_extractor.extract_score_features(self) + + def _load_performances(self): + for perf_midi_name in self.meta.perform_lists: + perform_data = PerformData(perf_midi_name, self.meta) + self._align_perform_with_score(perform_data) + self.performances.append(perform_data) + + def _align_perform_with_score(self, perform): + perform.match_between_xml_perf = matching.match_score_pair2perform(self.score.score_pairs, perform.midi_notes, perform.corresp) + perform.pairs = matching.make_xml_midi_pair(self.score.xml_notes, perform.midi_notes, perform.match_between_xml_perf) + perform.pairs, perform.valid_position_pairs = matching.make_available_xml_midi_positions(perform.pairs) + + print('Performance path is ', perform.midi_path) + perform._count_matched_notes() + + def __str__(self): + text = 'Path name: {}, Composer Name: {}, Number of Performances: {}'.format(self.meta.xml_path, self.meta.composer, len(self.performances)) + return text + + +# score meta data class +class PieceMeta: + def __init__(self, xml_path, perform_lists, score_midi_path, composer=None): + self.xml_path = xml_path + self.folder_path = os.path.dirname(xml_path) + self.composer = composer + self.pedal_elongate = False + self.perform_lists = perform_lists + self.score_midi_path = score_midi_path + + def __str__(self): + return str(self.__dict__) + + def _check_perf_align(self): + # TODO: better to move PieceData? + aligned_perf = [] + for perf in self.perform_lists: + align_file_name = os.path.splitext(perf)[0] + '_infer_corresp.txt' + # align_file_name = Path(perf).parent / (Path(perf).stem + '_infer_corresp.txt') + print(align_file_name) + if os.path.isfile(align_file_name): + aligned_perf.append(perf) + continue + self.align_score_and_perf_with_nakamura(os.path.abspath(perf), self.score_midi_path) + if os.path.isfile(align_file_name): # check once again whether the alignment was successful + aligned_perf.append(perf) + + self.perf_file_list = aligned_perf + + def align_score_and_perf_with_nakamura(self, midi_file_path, score_midi_path): + file_folder, file_name = ntpath.split(midi_file_path) + perform_midi = midi_file_path + + shutil.copy(perform_midi, os.path.join(align_dir, 'infer.mid')) + shutil.copy(score_midi_path, os.path.join(align_dir, 'score.mid')) + current_dir = os.getcwd() + try: + os.chdir(align_dir) + subprocess.check_call(["sh", "MIDIToMIDIAlign.sh", "score", "infer"]) + except: + print('Error to process {}'.format(midi_file_path)) + print('Trying to fix MIDI file {}'.format(midi_file_path)) + os.chdir(current_dir) + shutil.copy(midi_file_path, midi_file_path+'old') + midi_utils.to_midi_zero(midi_file_path, save_midi=True, save_name=midi_file_path) + shutil.copy(midi_file_path, os.path.join(align_dir, 'infer.mid')) + try: + os.chdir(align_dir) + subprocess.check_call(["sh", "MIDIToMIDIAlign.sh", "score", "infer"]) + except subprocess.CalledProcessError as error: + print(error) + align_success = False + print('Fail to process {}'.format(midi_file_path)) + os.chdir(current_dir) + else: + align_success = True + else: + align_success = True + + if align_success: + shutil.move('infer_corresp.txt', midi_file_path.replace('.mid', '_infer_corresp.txt')) + shutil.move('infer_match.txt', midi_file_path.replace('.mid', '_infer_match.txt')) + shutil.move('infer_spr.txt', midi_file_path.replace('.mid', '_infer_spr.txt')) + shutil.move('score_spr.txt', os.path.join(align_dir, '_score_spr.txt')) + shutil.copy('score_fmt3x.txt', score_midi_path.replace('.mid', 'score_fmt3x.txt')) + os.chdir(current_dir) + + +# performance data class +class PerformData: + def __init__(self, midi_path, meta): + self.midi_path = midi_path + self.midi = midi_utils.to_midi_zero(self.midi_path) + self.midi = midi_utils.add_pedal_inf_to_notes(self.midi) + self.midi_notes = self.midi.instruments[0].notes + self.corresp_path = os.path.splitext(self.midi_path)[0] + '_infer_corresp.txt' + self.corresp = matching.read_corresp(self.corresp_path) + self.perform_features = {} + self.match_between_xml_perf = None + + self.pairs = [] + self.valid_position_pairs = [] + + self.num_matched_notes = 0 + self.num_unmatched_notes = 0 + self.tempos = [] + + self.meta = meta + + def __str__(self): + return str(self.__dict__) + + def _count_matched_notes(self): + self.num_matched_notes = 0 + self.num_unmatched_notes = 0 + for pair in self.pairs: + if pair == []: + self.num_unmatched_notes += 1 + else: + self.num_matched_notes += 1 + print( + 'Number of Matched Notes: ' + str(self.num_matched_notes) + ', unmatched notes: ' + str(self.num_unmatched_notes)) + + +class ScoreData: + def __init__(self, xml_path, score_midi_path, read_xml_only=False): + self.xml_obj = None + self.xml_notes = None + self.num_notes = 0 + + # self.score_performance_match = [] + self.notes_graph = [] + self.score_midi_notes = [] + self.score_match_list = [] + self.score_pairs = [] + self.measure_positions = [] + self.beat_positions = [] + self.section_positions = [] + + self._load_score_xml(xml_path) + if not read_xml_only: + self._load_or_make_score_midi(score_midi_path) + self._match_score_xml_to_midi() + + def __str__(self): + return str(self.__dict__) + + def _load_score_xml(self, xml_path): + self.xml_obj = MusicXMLDocument(xml_path) + self._get_direction_encoded_notes() + self.notes_graph = score_graph.make_edge(self.xml_notes) + self.measure_positions = self.xml_obj.get_measure_positions() + self.beat_positions = self.xml_obj.get_beat_positions() + self.section_positions = xml_utils.find_tempo_change(self.xml_notes) + + def _get_direction_encoded_notes(self): + notes, rests = self.xml_obj.get_notes() + directions = self.xml_obj.get_directions() + time_signatures = self.xml_obj.get_time_signatures() + + self.xml_notes = xml_utils.apply_directions_to_notes(notes, directions, time_signatures) + self.num_notes = len(self.xml_notes) + + def _load_or_make_score_midi(self, score_midi_path): + print(score_midi_path) + if not os.path.isfile(score_midi_path): + self.make_score_midi(score_midi_path) + self.score_midi = midi_utils.to_midi_zero(score_midi_path) + self.score_midi_notes = self.score_midi.instruments[0].notes + self.score_midi_notes.sort(key=lambda x:x.start) + + def make_score_midi(self, midi_file_name): + midi_notes, midi_pedals = xml_utils.xml_notes_to_midi(self.xml_notes) + xml_utils.save_midi_notes_as_piano_midi(midi_notes, [], midi_file_name, bool_pedal=True) + + def _match_score_xml_to_midi(self): + self.score_match_list = matching.match_xml_to_midi(self.xml_notes, self.score_midi_notes) + self.score_pairs = matching.make_xml_midi_pair(self.xml_notes, self.score_midi_notes, self.score_match_list) + + +class YamahaDataset(DataSet): + def __init__(self, path, save): + super().__init__(path, save=save) + + def load_data(self): + path = Path(self.path) + xml_list = sorted(path.glob('**/*.musicxml')) + score_midis = [xml.parent / 'midi_cleaned.mid' for xml in xml_list] + composers = [xml.relative_to(self.path).parts[0] for xml in xml_list] + + perform_lists = [] + for xml in xml_list: + midis = sorted(xml.parent.glob('*.mid')) + sorted(xml.parent.glob('*.MID')) + midis = [str(midi) for midi in midis if midi.name not in ['midi.mid', 'midi_cleaned.mid']] + midis = [midi for midi in midis if not 'XP' in midi] + perform_lists.append(midis) + + # Path -> string wrapper + xml_list = [str(xml) for xml in xml_list] + score_midis = [str(midi) for midi in score_midis] + return xml_list, score_midis, perform_lists, composers + + +class EmotionDataset(DataSet): + def __init__(self, path, save=False): + super().__init__(path, save=save) + + def load_data(self): + path = Path(self.path) + xml_list = sorted(path.glob('**/*.musicxml')) + score_midis = [xml.stem + '_midi_cleaned.mid' for xml in xml_list] + composers = [xml.stem.split('.')[0] for xml in xml_list] + + perform_lists = [] + for xml in xml_list: + midis = sorted(xml.parent.glob(f'{xml.stem}*.mid')) + midis = [str(midi) for midi in midis if midi.name not in ['midi.mid', 'midi_cleaned.mid']] + perform_lists.append(midis) + + # Path -> string wrapper + xml_list = [str(xml) for xml in xml_list] + score_midis = [str(midi) for midi in score_midis] + return xml_list, score_midis, perform_lists, composers + + +class StandardDataset(DataSet): + def __init__(self, path, save=False): + super().__init__(path, save=save) + + def load_data(self): + path = Path(self.path) + xml_list = sorted(path.glob('**/*.musicxml')) + score_midis = [xml.parent / 'midi_cleaned.mid' for xml in xml_list] + composers = [xml.relative_to(self.path).parts[0] for xml in xml_list] + + perform_lists = [] + for xml in xml_list: + midis = sorted(xml.parent.glob('*.mid')) + sorted(xml.parent.glob('*.MID')) + midis = [str(midi) for midi in midis if midi.name not in ['midi.mid', 'midi_cleaned.mid']] + midis = [midi for midi in midis if not 'XP' in midi] + perform_lists.append(midis) + + # Path -> string wrapper + xml_list = [str(xml) for xml in xml_list] + score_midis = [str(midi) for midi in score_midis] + return xml_list, score_midis, perform_lists, composers + diff --git a/data_for_training.py b/data_for_training.py new file mode 100644 index 0000000..50df8d0 --- /dev/null +++ b/data_for_training.py @@ -0,0 +1,249 @@ +import os +import random +import numpy as np +import pickle +from . import dataset_split +from pathlib import Path +from tqdm import tqdm + +NORM_FEAT_KEYS = ('midi_pitch', 'duration', 'beat_importance', 'measure_length', 'qpm_primo', + 'following_rest', 'distance_from_abs_dynamic', 'distance_from_recent_tempo', + 'beat_tempo', 'velocity', 'onset_deviation', 'articulation', 'pedal_refresh_time', + 'pedal_cut_time', 'pedal_at_start', 'pedal_at_end', 'soft_pedal', + 'pedal_refresh', 'pedal_cut', 'qpm_primo') + +VNET_COPY_DATA_KEYS = ('note_location', 'align_matched', 'articulation_loss_weight') +VNET_INPUT_KEYS = ('midi_pitch', 'duration', 'beat_importance', 'measure_length', 'qpm_primo', + 'following_rest', 'distance_from_abs_dynamic', 'distance_from_recent_tempo', + 'beat_position', 'xml_position', 'grace_order', 'preceded_by_grace_note', + 'followed_by_fermata_rest', 'pitch', 'tempo', 'dynamic', 'time_sig_vec', + 'slur_beam_vec', 'composer_vec', 'notation', 'tempo_primo') + +VNET_OUTPUT_KEYS = ('beat_tempo', 'velocity', 'onset_deviation', 'articulation', 'pedal_refresh_time', + 'pedal_cut_time', 'pedal_at_start', 'pedal_at_end', 'soft_pedal', + 'pedal_refresh', 'pedal_cut', 'qpm_primo', 'beat_tempo', 'beat_dynamics', + 'measure_tempo', 'measure_dynamics') + + +class ScorePerformPairData: + def __init__(self, piece, perform): + self.piece_path = piece.meta.xml_path + self.perform_path = perform.midi_path + self.graph_edges = piece.notes_graph + self.features = {**piece.score_features, **perform.perform_features} + self.split_type = None + self.features['num_notes'] = piece.num_notes + + +class PairDataset: + def __init__(self, dataset): + self.dataset_path = dataset.path + self.data_pairs = [] + self.feature_stats = None + for piece in dataset.pieces: + for performance in piece.performances: + self.data_pairs.append(ScorePerformPairData(piece, performance)) + + def get_squeezed_features(self, target_feat_keys): + squeezed_values = dict() + for feat_type in target_feat_keys: + squeezed_values[feat_type] = [] + for pair in self.data_pairs: + for feat_type in target_feat_keys: + if isinstance(pair.features[feat_type], list): + squeezed_values[feat_type] += pair.features[feat_type] + else: + squeezed_values[feat_type].append(pair.features[feat_type]) + return squeezed_values + + def update_mean_stds_of_entire_dataset(self, target_feat_keys=NORM_FEAT_KEYS): + squeezed_values = self.get_squeezed_features(target_feat_keys) + self.feature_stats = cal_mean_stds(squeezed_values, target_feat_keys) + + def update_dataset_split_type(self, valid_set_list=dataset_split.VALID_LIST, test_set_list=dataset_split.TEST_LIST): + # TODO: the split + for pair in self.data_pairs: + path = pair.piece_path + for valid_name in valid_set_list: + if valid_name in path: + pair.split_type = 'valid' + break + else: + for test_name in test_set_list: + if test_name in path: + pair.split_type = 'test' + break + + if pair.split_type is None: + pair.split_type = 'train' + + def shuffle_data(self): + random.shuffle(self.data_pairs) + + def save_features_for_virtuosoNet(self, save_folder): + ''' + Convert features into format of VirtuosoNet training data + :return: None (save file) + ''' + def _flatten_path(file_path): + return '_'.join(file_path.parts) + + save_folder = Path(save_folder) + split_types = ['train', 'valid', 'test'] + + save_folder.mkdir() + for split in split_types: + (save_folder / split).mkdir() + + training_data = [] + validation_data = [] + test_data = [] + + for pair_data in tqdm(self.data_pairs): + formatted_data = dict() + formatted_data['input_data'], formatted_data['output_data'] = convert_feature_to_VirtuosoNet_format(pair_data.features, self.feature_stats) + for key in VNET_COPY_DATA_KEYS: + formatted_data[key] = pair_data.features[key] + formatted_data['graph'] = pair_data.graph_edges + formatted_data['score_path'] = pair_data.piece_path + formatted_data['perform_path'] = pair_data.perform_path + + save_name = _flatten_path( + Path(pair_data.perform_path).relative_to(Path(self.dataset_path))) + '.dat' + + with open(save_folder / pair_data.split_type / save_name, "wb") as f: + pickle.dump(formatted_data, f, protocol=2) + + with open(save_folder / "stat.dat", "wb") as f: + pickle.dump(self.feature_stats, f, protocol=2) + + +def get_feature_from_entire_dataset(dataset, target_score_features, target_perform_features): + # e.g. feature_type = ['score', 'duration'] or ['perform', 'beat_tempo'] + output_values = dict() + for feat_type in (target_score_features + target_perform_features): + output_values[feat_type] = [] + for piece in dataset.pieces: + for performance in piece.performances: + for feat_type in target_score_features: + # output_values[feat_type] += piece.score_features[feat_type] + output_values[feat_type].append(piece.score_features[feat_type]) + for feat_type in target_perform_features: + output_values[feat_type].append(performance.perform_features[feat_type]) + return output_values + + +def normalize_feature(data_values, target_feat_keys): + for feat in target_feat_keys: + concatenated_data = [note for perf in data_values[feat] for note in perf] + mean = sum(concatenated_data) / len(concatenated_data) + var = sum(pow(x-mean,2) for x in concatenated_data) / len(concatenated_data) + # data_values[feat] = [(x-mean) / (var ** 0.5) for x in data_values[feat]] + for i, perf in enumerate(data_values[feat]): + data_values[feat][i] = [(x-mean) / (var ** 0.5) for x in perf] + + return data_values + +# def combine_dict_to_array(): + +def cal_mean_stds_of_entire_dataset(dataset, target_features): + ''' + :param dataset: DataSet class + :param target_features: list of dictionary keys of features + :return: dictionary of mean and stds + ''' + output_values = dict() + for feat_type in (target_features): + output_values[feat_type] = [] + + for piece in dataset.pieces: + for performance in piece.performances: + for feat_type in target_features: + if feat_type in piece.score_features: + output_values[feat_type] += piece.score_features[feat_type] + elif feat_type in performance.perform_features: + output_values[feat_type] += performance.perform_features[feat_type] + else: + print('Selected feature {} is not in the data'.format(feat_type)) + + stats = cal_mean_stds(output_values, target_features) + + return stats + + +def cal_mean_stds(feat_datas, target_features): + stats = dict() + for feat_type in target_features: + mean = sum(feat_datas[feat_type]) / len(feat_datas[feat_type]) + var = sum((x-mean)**2 for x in feat_datas[feat_type]) / len(feat_datas[feat_type]) + stds = var ** 0.5 + if stds == 0: + stds = 1 + stats[feat_type] = {'mean': mean, 'stds':stds} + return stats + + +def convert_feature_to_VirtuosoNet_format(feature_data, stats, input_keys=VNET_INPUT_KEYS, output_keys=VNET_OUTPUT_KEYS): + input_data = [] + output_data = [] + + def check_if_global_and_normalize(key): + value = feature_data[key] + if not isinstance(value, list) or len(value) != feature_data['num_notes']: # global features like qpm_primo, tempo_primo, composer_vec + value = [value] * feature_data['num_notes'] + if key in stats: # if key needs normalization, + value = [(x - stats[key]['mean']) / stats[key]['stds'] for x in value] + return value + + def add_to_list(alist, item): + if isinstance(item, list): + alist += item + else: + alist.append(item) + return alist + + def cal_dimension(data_with_all_features): + total_length = 0 + for feat_data in data_with_all_features: + if isinstance(feat_data[0], list): + length = len(feat_data[0]) + else: + length = 1 + total_length += length + return total_length + + + for key in input_keys: + value = check_if_global_and_normalize(key) + input_data.append(value) + for key in output_keys: + value = check_if_global_and_normalize(key) + output_data.append(value) + + input_dimension = cal_dimension(input_data) + output_dimension = cal_dimension(output_data) + + input_array = np.zeros((feature_data['num_notes'], input_dimension)) + output_array = np.zeros((feature_data['num_notes'], output_dimension)) + + current_idx = 0 + + for value in input_data: + if isinstance(value[0], list): + length = len(value[0]) + input_array[:, current_idx:current_idx + length] = value + else: + length = 1 + input_array[:,current_idx] = value + current_idx += length + current_idx = 0 + for value in output_data: + if isinstance(value[0], list): + length = len(value[0]) + output_array[:, current_idx:current_idx + length] = value + else: + length = 1 + output_array[:,current_idx] = value + current_idx += length + + return input_array, output_array diff --git a/data_generation.py b/data_generation.py index 281bc63..f1ecdb0 100644 --- a/data_generation.py +++ b/data_generation.py @@ -1,13 +1,14 @@ from __future__ import division import pickle import random -import xml_matching import copy import pandas import numpy as np import argparse import os -import model_constants as cons + +from . import xml_matching as xml_matching +from . import dataset_split as split parser = argparse.ArgumentParser() parser.add_argument('--regression', default=True, type=lambda x: (str(x).lower() == 'true')) @@ -20,8 +21,8 @@ REGRESSION = args.regression print('Data type is regression: ', args.regression) -VALID_LIST = cons.VALID_LIST -TEST_LIST = cons.TEST_LIST +VALID_LIST = split.VALID_LIST +TEST_LIST = split.TEST_LIST def save_features_as_vector(dataset, num_train, num_valid, save_name): complete_xy = [] @@ -33,49 +34,17 @@ def save_features_as_vector(dataset, num_train, num_valid, save_name): num_piece += 1 for perform in piece: num_perform +=1 - train_x = [] - train_y = [] - align_matched_status = [] - pedal_status = [] - # is_beat_list = [] - # beat_numbers = [] - # measure_numbers = [] - # voice_numbers = [] - note_locations = [] features = perform['features'] score = perform['score'] composer_vec = perform['composer'] score_graph = perform['graph'] - for feature in features: - total_notes += 1 - if not feature.qpm == None: - train_x.append( - [feature.midi_pitch, feature.duration, feature.beat_importance, feature.measure_length, - feature.qpm_primo, feature.following_rest, feature.distance_from_abs_dynamic, - feature.distance_from_recent_tempo, feature.beat_position, feature.xml_position, - feature.grace_order, feature.preceded_by_grace_note, feature.followed_by_fermata_rest] - + feature.pitch + feature.tempo + feature.dynamic + feature.time_sig_vec + - feature.slur_beam_vec + composer_vec + feature.notation + feature.tempo_primo) - - temp_y = [feature.qpm, feature.velocity, feature.xml_deviation, - feature.articulation, feature.pedal_refresh_time, feature.pedal_cut_time, - feature.pedal_at_start, feature.pedal_at_end, feature.soft_pedal, - feature.pedal_refresh, - feature.pedal_cut, feature.qpm, feature.beat_dynamic, feature.measure_tempo, feature.measure_dynamic] \ - + feature.trill_param - - train_y.append(temp_y) - align_matched_status.append(feature.align_matched) - pedal_status.append(feature.articulation_loss_weight) - # prev_feat[0] = feature.previous_tempo - num_total_datapoint += 1 - note_loc = feature.note_location - note_locations.append(note_loc) - # is_beat_list.append(feature.is_beat) - # beat_numbers.append(feature.beat_index) - # measure_numbers.append(feature.measure_index) - # voice_numbers.append(feature.voice) + train_x, train_y = xml_matching.convert_features_to_vector(features, composer_vec) + align_matched_status = [f.align_matched for f in features] + pedal_status = [f.articulation_loss_weight for f in features] + note_locations = [f.note_location for f in features] + total_notes += len(train_x) + # windowed_train_x = make_windowed_data(train_x, input_length ) # complete_xy.append([train_x, train_y, previous_y, beat_numbers, measure_numbers, voice_numbers]) complete_xy.append([train_x, train_y, note_locations, align_matched_status, pedal_status, score_graph, score]) @@ -90,6 +59,8 @@ def save_features_as_vector(dataset, num_train, num_valid, save_name): print('Total data point is ', num_total_datapoint) print('Number of total piece is ', num_piece, ' and total performance is ', num_perform) + print('Number of training perform is ', num_train, ' number of valid perform is', num_valid, ' and test performance is ', len(complete_xy) - num_train - num_valid) + print(total_notes) num_input = len(train_x[0]) num_output = len(train_y[0]) @@ -276,7 +247,7 @@ def key_augmentation(data_x, key_change): return data_x_aug -def load_entire_subfolder(path): +def load_entire_subfolder(path, minimum_perform_limit=0): entire_pairs = [] num_train_pairs = 0 num_valid_pairs = 0 @@ -297,11 +268,10 @@ def load_entire_subfolder(path): break if not skip: xml_name = foldername + 'musicxml_cleaned.musicxml' - if os.path.isfile(xml_name): print(foldername) piece_pairs = xml_matching.load_pairs_from_folder(foldername) - if piece_pairs is not None: + if piece_pairs is not None and len(piece_pairs) > minimum_perform_limit: entire_pairs.append(piece_pairs) num_train_pairs += len(piece_pairs) @@ -314,7 +284,7 @@ def load_entire_subfolder(path): if os.path.isfile(xml_name): print(foldername) piece_pairs = xml_matching.load_pairs_from_folder(foldername) - if piece_pairs is not None: + if piece_pairs is not None and len(piece_pairs) > minimum_perform_limit: entire_pairs.append(piece_pairs) num_valid_pairs += len(piece_pairs) print('num valid pairs', num_valid_pairs) @@ -328,7 +298,7 @@ def load_entire_subfolder(path): if os.path.isfile(xml_name): print(foldername) piece_pairs = xml_matching.load_pairs_from_folder(foldername) - if piece_pairs is not None: + if piece_pairs is not None and len(piece_pairs) > minimum_perform_limit: entire_pairs.append(piece_pairs) num_test_pairs += len(piece_pairs) @@ -340,5 +310,5 @@ def load_entire_subfolder(path): # xml_matching.check_data_split('chopin_cleaned/') -chopin_pairs, num_train_pairs, num_valid_pairs, num_test_pairs = load_entire_subfolder('chopin_cleaned/') -save_features_as_vector(chopin_pairs, num_train_pairs, num_valid_pairs, 'icml_grace') \ No newline at end of file +chopin_pairs, num_train_pairs, num_valid_pairs, num_test_pairs = load_entire_subfolder('pyScoreParser/chopin_cleaned/', 4) +save_features_as_vector(chopin_pairs, num_train_pairs, num_valid_pairs, 'perform_style_set_5') \ No newline at end of file diff --git a/dataset_split.py b/dataset_split.py new file mode 100644 index 0000000..7c88ede --- /dev/null +++ b/dataset_split.py @@ -0,0 +1,48 @@ + +VALID_LIST =['Bach/Prelude/bwv_865/', + 'Bach/Prelude/bwv_874/', + 'Bach/Fugue/bwv_865/', + 'Bach/Fugue/bwv_874/', + 'Chopin/Etudes_op_10/10/', + 'Chopin/Etudes_op_25/4/', + 'Chopin/Scherzos/31/', + 'Chopin/Sonata_3/4th/', + 'Mozart/Piano_Sonatas/12-3/', + 'Haydn/Keyboard_Sonatas/46-1/', + 'Rachmaninoff/Preludes_op_23/4/', + 'Beethoven/Piano_Sonatas/3-1/', + 'Beethoven/Piano_Sonatas/12-1/', + 'Beethoven/Piano_Sonatas/15-4/', + 'Beethoven/Piano_Sonatas/21-2/', + 'Beethoven/Piano_Sonatas/30-1/', + 'Schumann/Kreisleriana/5/', + 'Schubert/Impromptu_op.90_D.899/2/', + 'Liszt/Annees_de_pelerinage_2/1_Gondoliera/', + 'Liszt/Transcendental_Etudes/4/', + 'Liszt/Concert_Etude_S145/2/', + ] + +TEST_LIST = ['Bach/Prelude/bwv_858/', + 'Bach/Prelude/bwv_891/', + 'Bach/Fugue/bwv_858/', + 'Bach/Fugue/bwv_891/', + 'Chopin/Etudes_op_10/2/', + 'Chopin/Etudes_op_10/12/', + 'Chopin/Etudes_op_25/12/', + # 'Chopin/Barcarolle/', + 'Chopin/Scherzos/39/', + 'Haydn/Keyboard_Sonatas/31-1/', + 'Haydn/Keyboard_Sonatas/49-1/', + 'Beethoven/Piano_Sonatas/5-1/', + 'Beethoven/Piano_Sonatas/7-2/', + 'Beethoven/Piano_Sonatas/17-1/', + 'Beethoven/Piano_Sonatas/17-1_no_repeat/', + 'Beethoven/Piano_Sonatas/27-1/', + 'Beethoven/Piano_Sonatas/31-2/', + 'Schubert/Impromptu_op.90_D.899/3/', + 'Schubert/Piano_Sonatas/664-1/', + 'Liszt/Transcendental_Etudes/5/', + 'Liszt/Transcendental_Etudes/9/', + 'Liszt/Gran_Etudes_de_Paganini/6_Theme_and_Variations/', + ] + diff --git a/feature_extraction.py b/feature_extraction.py new file mode 100644 index 0000000..bcb4bbb --- /dev/null +++ b/feature_extraction.py @@ -0,0 +1,574 @@ +import xml_direction_encoding as dir_enc +import xml_utils, utils, feature_utils +import copy +import math +import warnings + +class ScoreExtractor: + """ + """ + def __init__(self, feature_keys): + self.selected_feature_keys = feature_keys + + self.dyn_emb_tab = dir_enc.define_dyanmic_embedding_table() + self.tem_emb_tab = dir_enc.define_tempo_embedding_table() + + def extract_score_features(self, piece_data): + for key in self.selected_feature_keys: + piece_data.score_features[key] = getattr( + self, 'get_' + key)(piece_data) + + return piece_data.score_features + + def crescendo_to_continuous_value(self, note, feature): + cresc_words = ['cresc', 'decresc', 'dim'] + if feature.dynamic[1] != 0: + for rel in note.dynamic.relative: + for word in cresc_words: + if word in rel.type['type'] or word in rel.type['content']: + rel_length = rel.end_xml_position - rel.xml_position + if rel_length == float("inf") or rel_length == 0: + rel_length = note.state_fixed.divisions * 10 + ratio = (note.note_duration.xml_position - rel.xml_position) / rel_length + feature.dynamic[1] *= (ratio + 0.05) + break + + def get_note_location(self, piece_data): + # TODO: need check up + locations = [] + for _, note in enumerate(piece_data.xml_notes): + measure_index = note.measure_number - 1 + locations.append( + feature_utils.NoteLocation(beat=utils.binary_index(piece_data.beat_positions, note.note_duration.xml_position), + measure=measure_index, + voice=note.voice, + section=utils.binary_index(piece_data.section_positions, note.note_duration.xml_position))) + locations = feature_utils.make_index_continuous(locations) + return locations + + def get_qpm_primo(self, piece_data): + piece_data.qpm_primo = piece_data.xml_notes[0].state_fixed.qpm + return piece_data.qpm_primo + + def get_midi_pitch(self, piece_data): + return [note.pitch[1] for note in piece_data.xml_notes] + + def get_pitch(self, piece_data): + return [feature_utils.pitch_into_vector( + note.pitch[1]) for note in piece_data.xml_notes] + + def get_duration(self, piece_data): + return [note.note_duration.duration / note.state_fixed.divisions + for note in piece_data.xml_notes] + + def get_grace_order(self, piece_data): + return [note.note_duration.grace_order for note in piece_data.xml_notes] + + def get_is_grace_note(self, piece_data): + return [int(note.note_duration.is_grace_note) for note in piece_data.xml_notes] + + def get_preceded_by_grace_note(self, piece_data): + return [int(note.note_duration.preceded_by_grace_note) for note in piece_data.xml_notes] + + def get_time_sig_vec(self, piece_data): + return [feature_utils.time_signature_to_vector(note.tempo.time_signature) for note in piece_data.xml_notes] + + def get_following_rest(self, piece_data): + return [note.following_rest_duration / + note.state_fixed.divisions for note in piece_data.xml_notes] + + def get_followed_by_fermata_rest(self, piece_data): + return [int(note.followed_by_fermata_rest) for note in piece_data.xml_notes] + + def get_notation(self, piece_data): + return [feature_utils.note_notation_to_vector( + note) for note in piece_data.xml_notes] + + def get_slur_beam_vec(self, piece_data): + return [[int(note.note_notations.is_slur_start), + int(note.note_notations.is_slur_continue), + int(note.note_notations.is_slur_stop), + int(note.note_notations.is_beam_start), + int(note.note_notations.is_beam_continue), + int(note.note_notations.is_beam_stop)] + for note in piece_data.xml_notes] + + def get_dynamic(self, piece_data): + return [ + dir_enc.dynamic_embedding( + dir_enc.direction_words_flatten(note.dynamic), self.dyn_emb_tab, len_vec=4) + for note in piece_data.xml_notes] + + def get_tempo(self, piece_data): + return [ + dir_enc.dynamic_embedding( + dir_enc.direction_words_flatten(note.tempo), self.tem_emb_tab, len_vec=5) + for note in piece_data.xml_notes] + + def get_xml_position(self, piece_data): + total_length = xml_utils.cal_total_xml_length( + piece_data.xml_notes) + return [note.note_duration.xml_position / + total_length for note in piece_data.xml_notes] + + def get_beat_position(self, piece_data): + beat_positions = [] + for _, note in enumerate(piece_data.xml_notes): + measure_index = note.measure_number - 1 + note_position = note.note_duration.xml_position + + if measure_index + 1 < len(piece_data.measure_positions): + measure_length = piece_data.measure_positions[measure_index + + 1] - piece_data.measure_positions[measure_index] + else: + measure_length = piece_data.measure_positions[measure_index] - \ + piece_data.measure_positions[measure_index - 1] + beat_position = ( + note_position - piece_data.measure_positions[measure_index]) / measure_length + beat_positions.append(beat_position) + return beat_positions + + def get_beat_importance(self, piece_data): + try: + beat_positions = piece_data.score_features['beat_position'] + except: + beat_positions = self.get_beat_position(piece_data) + beat_importances = [] + for i, note in enumerate(piece_data.xml_notes): + importance = feature_utils.cal_beat_importance( + beat_positions[i], note.tempo.time_numerator) + beat_importances.append(importance) + return beat_importances + + def get_measure_length(self, piece_data): + measure_lengthes = [] + for _, note in enumerate(piece_data.xml_notes): + measure_index = note.measure_number - 1 + + if measure_index + 1 < len(piece_data.measure_positions): + measure_length = piece_data.measure_positions[measure_index + + 1] - piece_data.measure_positions[measure_index] + else: + measure_length = piece_data.measure_positions[measure_index] - \ + piece_data.measure_positions[measure_index - 1] + measure_lengthes.append( + measure_length / note.state_fixed.divisions) + return measure_lengthes + + def get_cresciuto(self, piece_data): + # This function converts cresciuto class information into single numeric value + cresciutos = [] + for note in piece_data.xml_notes: + if note.dynamic.cresciuto: + cresciuto = (note.dynamic.cresciuto.overlapped + 1) / 2 + if note.dynamic.cresciuto.type == 'diminuendo': + cresciuto *= -1 + else: + cresciuto = 0 + cresciutos.append(cresciuto) + return cresciutos + + def get_distance_from_abs_dynamic(self, piece_data): + return [(note.note_duration.xml_position - note.dynamic.absolute_position) + / note.state_fixed.divisions for note in piece_data.xml_notes] + + def get_distance_from_recent_tempo(self, piece_data): + return [(note.note_duration.xml_position - note.tempo.recently_changed_position) + / note.state_fixed.divisions for note in piece_data.xml_notes] + + def get_composer_vec(self, piece_data): + return feature_utils.composer_name_to_vec(piece_data.meta.composer) + + def get_tempo_primo(self, piece_data): + tempo_primo_word = dir_enc.direction_words_flatten( + piece_data.xml_notes[0].tempo) + if tempo_primo_word: + piece_data.tempo_primo = dir_enc.dynamic_embedding( + tempo_primo_word, self.tem_emb_tab, 5) + piece_data.tempo_primo = piece_data.tempo_primo[0:2] + else: + piece_data.tempo_primo = [0, 0] + return piece_data.tempo_primo + +class PerformExtractor: + def __init__(self, selected_feature_keys): + self.selected_feature_keys = selected_feature_keys + + def extract_perform_features(self, piece_data, perform_data): + # perform_data.perform_features = {} + for feature_key in self.selected_feature_keys: + perform_data.perform_features[feature_key] = getattr(self, 'get_' + feature_key)(piece_data, perform_data) + + return perform_data.perform_features + + def get_beat_tempo(self, piece_data, perform_data): + tempos = feature_utils.cal_tempo_by_positions(piece_data.beat_positions, perform_data.valid_position_pairs) + # update tempos for perform data + perform_data.tempos = tempos + return [math.log(utils.get_item_by_xml_position(tempos, note).qpm, 10) for note in piece_data.xml_notes] + + def get_measure_tempo(self, piece_data, perform_data): + tempos = feature_utils.cal_tempo_by_positions(piece_data.measure_positions, perform_data.valid_position_pairs) + return [math.log(utils.get_item_by_xml_position(tempos, note).qpm, 10) for note in piece_data.xml_notes] + + def get_section_tempo(self, piece_data, perform_data): + tempos = feature_utils.cal_tempo_by_positions(piece_data.section_positions, perform_data.valid_position_pairs) + return [math.log(utils.get_item_by_xml_position(tempos, note).qpm, 10) for note in piece_data.xml_notes] + + def get_qpm_primo(self, piece_data, perform_data, view_range=10): + if 'beat_tempo' not in perform_data.perform_features: + perform_data.perform_features['beat_tempo'] = self.get_beat_tempo(piece_data, perform_data) + qpm_primo = 0 + for i in range(view_range): + tempo = perform_data.tempos[i] + qpm_primo += tempo.qpm + + return math.log(qpm_primo / view_range, 10) + + def get_articulation(self, piece_data, perform_data): + features = [] + if 'beat_tempo' not in perform_data.perform_features: + perform_data.perform_features['beat_tempo'] = self.get_beat_tempo(piece_data, perform_data) + if 'trill_parameters' not in perform_data.perform_features: + perform_data.perform_features['trill_parameters'] = self.get_trill_parameters(piece_data, perform_data) + for i, pair in enumerate(perform_data.pairs): + if pair == []: + articulation = 0 + else: + note = pair['xml'] + midi = pair['midi'] + tempo = utils.get_item_by_xml_position(perform_data.tempos, note) + xml_duration = note.note_duration.duration + if xml_duration == 0: + articulation = 1 + elif note.is_overlapped: + articulation = 1 + else: + duration_as_quarter = xml_duration / note.state_fixed.divisions + second_in_tempo = duration_as_quarter / tempo.qpm * 60 + if note.note_notations.is_trill: + _, actual_second = xml_utils.find_corresp_trill_notes_from_midi(piece_data, perform_data, i) + else: + actual_second = midi.end - midi.start + articulation = actual_second / second_in_tempo + if actual_second == 0: + articulation = 1 + if articulation <= 0: + # print('Articulation error: tempo.qpm was{}, ') + # if articulation > 6: + print('check: articulation is {} in {}th note of perform {}. ' + 'Tempo QPM was {}, in-tempo duration was {} seconds and was performed in {} seconds' + .format(articulation, i , perform_data.midi_path, tempo.qpm, second_in_tempo, actual_second)) + print('xml_duration of note was {}'.format(xml_duration, note.state_fixed.divisions)) + print('midi start was {} and end was {}'.format(midi.start, midi.end)) + articulation = math.log(articulation, 10) + features.append(articulation) + + return features + + def get_onset_deviation(self, piece_data, perform_data): + ''' Deviation of individual note's onset + :param piece_data: PieceData class + :param perform_data: PerformData class + :return: note-level onset deviation of performance notes in quarter-notes + Onset deviation is defined as how much the note's onset is apart from its "in-tempo" position. + The "in-tempo" position is defined by pre-calculated beat-level tempo. + ''' + + features = [] + if 'beat_tempo' not in perform_data.perform_features: + perform_data.perform_features['beat_tempo'] = self.get_beat_tempo(piece_data, perform_data) + for pair in perform_data.pairs: + if pair == []: + deviation = 0 + else: + note = pair['xml'] + midi = pair['midi'] + tempo = utils.get_item_by_xml_position(perform_data.tempos, note) + tempo_start = tempo.time_position + + passed_duration = note.note_duration.xml_position - tempo.xml_position + actual_passed_second = midi.start - tempo_start + actual_passed_duration = actual_passed_second / 60 * tempo.qpm * note.state_fixed.divisions + + xml_pos_difference = actual_passed_duration - passed_duration + pos_diff_in_quarter_note = xml_pos_difference / note.state_fixed.divisions + # deviation_time = xml_pos_difference / note.state_fixed.divisions / tempo_obj.qpm * 60 + # if pos_diff_in_quarter_note >= 0: + # pos_diff_sqrt = math.sqrt(pos_diff_in_quarter_note) + # else: + # pos_diff_sqrt = -math.sqrt(-pos_diff_in_quarter_note) + # pos_diff_cube_root = float(pos_diff_ + deviation = pos_diff_in_quarter_note + features.append(deviation) + return features + + def get_align_matched(self, piece_data, perform_data): + features = [] + for pair in perform_data.pairs: + if pair == []: + matched = 0 + else: + matched = 1 + features.append(matched) + return features + + def get_velocity(self, piece_data, perform_data): + ''' + :param piece_data: + :param perform_data: + :return: List of MIDI velocities of notes in score-performance pair. + ''' + features = [] + prev_velocity = 64 + for pair in perform_data.pairs: + if pair == []: + velocity = prev_velocity + else: + velocity = pair['midi'].velocity + prev_velocity = velocity + features.append(velocity) + return features + + # TODO: get pedal _ can be simplified + + def get_pedal_at_start(self, piece_data, perform_data): + features = [] + prev_pedal = 0 + for pair in perform_data.pairs: + if pair == []: + pedal = prev_pedal + else: + pedal = feature_utils.pedal_sigmoid(pair['midi'].pedal_at_start) + prev_pedal = pedal + features.append(pedal) + return features + + def get_pedal_at_end(self, piece_data, perform_data): + features = [] + prev_pedal = 0 + for pair in perform_data.pairs: + if pair == []: + pedal = prev_pedal + else: + pedal = feature_utils.pedal_sigmoid(pair['midi'].pedal_at_end) + prev_pedal = pedal + features.append(pedal) + return features + + def get_pedal_refresh(self, piece_data, perform_data): + features = [] + for pair in perform_data.pairs: + if pair == []: + pedal = 0 + else: + pedal = feature_utils.pedal_sigmoid(pair['midi'].pedal_refresh) + features.append(pedal) + return features + + def get_pedal_refresh_time(self, piece_data, perform_data): + features = [] + for pair in perform_data.pairs: + if pair == []: + pedal = 0 + else: + pedal = feature_utils.pedal_sigmoid(pair['midi'].pedal_refresh_time) + features.append(pedal) + return features + + def get_pedal_cut(self, piece_data, perform_data): + features = [] + prev_pedal = 0 + for pair in perform_data.pairs: + if pair == []: + pedal = prev_pedal + else: + pedal = feature_utils.pedal_sigmoid(pair['midi'].pedal_cut) + prev_pedal = pedal + features.append(pedal) + return features + + def get_pedal_cut_time(self, piece_data, perform_data): + features = [] + for pair in perform_data.pairs: + if pair == []: + pedal = 0 + else: + pedal = feature_utils.pedal_sigmoid(pair['midi'].pedal_cut_time) + features.append(pedal) + return features + + def get_soft_pedal(self, piece_data, perform_data): + features = [] + prev_pedal = 0 + for pair in perform_data.pairs: + if pair == []: + pedal = prev_pedal + else: + pedal = feature_utils.pedal_sigmoid(pair['midi'].soft_pedal) + prev_pedal = pedal + features.append(pedal) + return features + + def get_attack_deviation(self, piece, perform): + previous_xml_onset = 0 + previous_onset_timings = [] + previous_onset_indices = [] + attack_deviations = [0] * len(perform.pairs) + for i, pair in enumerate(perform.pairs): + if pair == []: + attack_deviations[i] = 0 + continue + if pair['xml'].note_duration.xml_position > previous_xml_onset: + if previous_onset_timings != []: + avg_onset_time = sum(previous_onset_timings) / len(previous_onset_timings) + for j, prev_idx in enumerate(previous_onset_indices): + attack_deviations[prev_idx] = abs(previous_onset_timings[j] - avg_onset_time) + previous_onset_timings = [] + previous_onset_indices = [] + + previous_onset_timings.append(pair['midi'].start) + previous_onset_indices.append(i) + previous_xml_onset = pair['xml'].note_duration.xml_position + + if previous_onset_timings != []: + avg_onset_time = sum(previous_onset_timings) / len(previous_onset_timings) + for j, prev_idx in enumerate(previous_onset_indices): + attack_deviations[prev_idx] = abs(previous_onset_timings[j] - avg_onset_time) + + return attack_deviations + + def get_non_abs_attack_deviation(self, piece, perform): + previous_xml_onset = 0 + previous_onset_timings = [] + previous_onset_indices = [] + attack_deviations = [0] * len(perform.pairs) + for i, pair in enumerate(perform.pairs): + if pair == []: + attack_deviations[i] = 0 + continue + if pair['xml'].note_duration.xml_position > previous_xml_onset: + if previous_onset_timings != []: + avg_onset_time = sum(previous_onset_timings) / len(previous_onset_timings) + for j, prev_idx in enumerate(previous_onset_indices): + attack_deviations[prev_idx] = previous_onset_timings[j] - avg_onset_time + previous_onset_timings = [] + previous_onset_indices = [] + + previous_onset_timings.append(pair['midi'].start) + previous_onset_indices.append(i) + previous_xml_onset = pair['xml'].note_duration.xml_position + + if previous_onset_timings != []: + avg_onset_time = sum(previous_onset_timings) / len(previous_onset_timings) + for j, prev_idx in enumerate(previous_onset_indices): + attack_deviations[prev_idx] = previous_onset_timings[j] - avg_onset_time + + return attack_deviations + + def get_tempo_fluctuation(self, piece, perform): + tempo_fluctuations = [None] * len(perform.pairs) + for i in range(1, len(perform.pairs)): + prev_qpm = perform.perform_features['beat_tempo'][i - 1] + curr_qpm = perform.perform_features['beat_tempo'][i] + if curr_qpm == prev_qpm: + continue + else: + tempo_fluctuations[i] = abs(curr_qpm - prev_qpm) + return tempo_fluctuations + + def get_abs_deviation(self, piece, perform): + return [abs(x) for x in perform.perform_features['onset_deviation']] + + def get_left_hand_velocity(self, piece, perform): + features = [None] * len(perform.pairs) + for i, pair in enumerate(perform.pairs): + if pair != [] and pair['xml'].staff == 2: + features[i] = pair['midi'].velocity + return features + + def get_right_hand_velocity(self, piece, perform): + features = [None] * len(perform.pairs) + for i, pair in enumerate(perform.pairs): + if pair != [] and pair['xml'].staff == 1: + features[i] = pair['midi'].velocity + return features + + def get_articulation_loss_weight(self, piece_data, perform_data): + if 'pedal_at_end' not in perform_data.perform_features: + perform_data.perform_features['pedal_at_end'] = self.get_pedal_at_end(piece_data, perform_data) + if 'pedal_refresh' not in perform_data.perform_features: + perform_data.perform_features['pedal_refresh'] = self.get_pedal_at_end(piece_data, perform_data) + features = [] + for pair, pedal, pedal_refresh in zip(perform_data.pairs, + perform_data.perform_features['pedal_at_end'], + perform_data.perform_features['pedal_refresh']): + if pair == []: + articulation_loss_weight = 0 + elif pedal > 70: + articulation_loss_weight = 0.05 + elif pedal > 60: + articulation_loss_weight = 0.5 + else: + articulation_loss_weight = 1 + + if pedal > 64 and pedal_refresh < 64: + # pedal refresh occurs in the note + articulation_loss_weight = 1 + + features.append(articulation_loss_weight) + return features + + def get_trill_parameters(self, piece_data, perform_data): + features = [] + for i, note in enumerate(piece_data.xml_notes): + if note.note_notations.is_trill: + trill_parameter, _ = xml_utils.find_corresp_trill_notes_from_midi(piece_data, perform_data, i) + else: + trill_parameter = [0, 0, 0, 0, 0] + features.append(trill_parameter) + + return features + + def get_left_hand_attack_deviation(self, piece, perform): + if 'non_abs_attack_deviation' not in perform.perform_features: + perform.perform_features['non_abs_attack_deviation'] = self.get_non_abs_attack_deviation(piece, perform) + features = [None] * len(perform.pairs) + for i, pair in enumerate(perform.pairs): + if pair != [] and pair['xml'].staff == 2: + features[i] = perform.perform_features['non_abs_attack_deviation'][i] + return features + + def get_right_hand_attack_deviation(self, piece, perform): + if 'non_abs_attack_deviation' not in perform.perform_features: + perform.perform_features['non_abs_attack_deviation'] = self.get_non_abs_attack_deviation(piece, perform) + features = [None] * len(perform.pairs) + for i, pair in enumerate(perform.pairs): + if pair != [] and pair['xml'].staff == 1: + features[i] = perform.perform_features['non_abs_attack_deviation'][i] + return features + + def get_beat_dynamics(self, piece_data, perform_data): + if 'velocity' not in perform_data.perform_features: + perform_data.perform_features['velocity'] = self.get_velocity(piece_data, perform_data) + if 'align_matched' not in perform_data.perform_features: + perform_data.perform_features['align_matched'] = self.get_align_matched(piece_data, perform_data) + if 'note_location' not in piece_data.score_features: + score_extractor = ScoreExtractor(['note_location']) + piece_data.score_features = score_extractor.extract_score_features(piece_data) + return feature_utils.get_longer_level_dynamics(perform_data.perform_features, piece_data.score_features['note_location'], length='beat') + + def get_measure_dynamics(self, piece_data, perform_data): + if 'velocity' not in perform_data.perform_features: + perform_data.perform_features['velocity'] = self.get_velocity(piece_data, perform_data) + if 'align_matched' not in perform_data.perform_features: + perform_data.perform_features['align_matched'] = self.get_align_matched(piece_data, perform_data) + if 'note_location' not in piece_data.score_features: + score_extractor = ScoreExtractor(['note_location']) + piece_data.score_features = score_extractor.extract_score_features(piece_data) + + return feature_utils.get_longer_level_dynamics(perform_data.perform_features, + piece_data.score_features['note_location'], length='measure') + + def get_staff(self, piece_data, perform_data): + return [note.staff for note in piece_data.xml_notes] \ No newline at end of file diff --git a/feature_utils.py b/feature_utils.py new file mode 100644 index 0000000..d9af642 --- /dev/null +++ b/feature_utils.py @@ -0,0 +1,357 @@ +""" Utilities for feature extraction + +Interface summary: + + import feature_utils + + pitch_vector = feature_utils.pitch_into_vector(pitch) + +call in feature_extraction.py + +get feature information to generate or modify feature +""" + +import math +import utils + +def cal_beat_importance(beat_position, numerator): + """ Returns beat importance in integer + + Args: + beat_position (integer): [0-1), note's relative position in measure + numerator (integer): note tempo's time numerator + + Returns: + beat_importance (integer): importance of each beat in integer format + + Example: + (in feature_extraction.py -> ScoreExtractor().extract_score_features()._get_beat_importance()) + >>> beat_positions = _get_beat_position(piece_data) + >>> for i, note in enumerate(piece_data.xml_notes): + >>> importance = feature_utils.cal_beat_importance( + beat_positions[i], note.tempo.time_numerator) + + """ + if beat_position == 0: + beat_importance = 4 + elif beat_position == 0.5 and numerator in [2, 4, 6, 12]: + beat_importance = 3 + elif abs(beat_position - (1/3)) < 0.001 and numerator in [3, 9]: + beat_importance = 2 + elif (beat_position * 4) % 1 == 0 and numerator in [2, 4]: + beat_importance = 1 + elif (beat_position * 5) % 1 == 0 and numerator in [5]: + beat_importance = 2 + elif (beat_position * 6) % 1 == 0 and numerator in [3, 6, 12]: + beat_importance = 1 + elif (beat_position * 8) % 1 == 0 and numerator in [2, 4]: + beat_importance = 0.5 + elif (beat_position * 9) % 1 == 0 and numerator in [9]: + beat_importance = 1 + elif (beat_position * 12) % 1 == 0 and numerator in [3, 6, 12]: + beat_importance = 0.5 + elif numerator == 7: + if abs((beat_position * 7) - 2) < 0.001: + beat_importance = 2 + elif abs((beat_position * 5) - 2) < 0.001: + beat_importance = 2 + else: + beat_importance = 0 + else: + beat_importance = 0 + return beat_importance + + +def pitch_into_vector(pitch): + """ Returns pitch vector from midi pitch value + octave value is normalized + + Args: + pitch (integer) : pitch value in midi number + + Returns: + pitch_vec (1-D list) : vector with [octave value, 12-class 1-hot vector] in shape (13, ) + + Example: + (in feature_extraction.py -> ScoreExtractor().extract_score_features()) + >>> features['pitch'] = [feature_utils.pitch_into_vector( + note.pitch[1]) for note in piece_data.xml_notes] + """ + # TODO: should be located in general file. maybe utils? + pitch_vec = [0] * 13 # octave + pitch class + octave = (pitch // 12) - 1 + octave = (octave - 4) / 4 # normalization + pitch_class = pitch % 12 + + pitch_vec[0] = octave + pitch_vec[pitch_class+1] = 1 + + return pitch_vec + + +def time_signature_to_vector(time_signature): + """ Returns + + Args: + time_signature + + Returns: + time signature vector (1-D list) + : appended list of numerator_vec and denominator_vec in shape (9, ) + numerator_vec (1-D list) : multi-hot vector correspond to each numerator value (integer) in shape (5, ) + denominator_vec (1-D list) : one-hot vector correspond to each denominator value (integer) in shape (4, ) + + Example: + (in feature_extraction.py -> ScoreExtractor().extract_score_features()) + >>> features['time_sig_vec'] = [feature_utils.time_signature_to_vector( + note.tempo.time_signature) for note in piece_data.xml_notes] + """ + numerator = time_signature.numerator + denominator = time_signature.denominator + + denominator_list = [2, 4, 8, 16] + numerator_vec = [0] * 5 + denominator_vec = [0] * 4 + + if denominator == 32: + denominator_vec[-1] = 1 + else: + denominator_type = denominator_list.index(denominator) + denominator_vec[denominator_type] = 1 + + if numerator == 2: + numerator_vec[0] = 1 + elif numerator == 3: + numerator_vec[1] = 1 + elif numerator == 4: + numerator_vec[0] = 1 + numerator_vec[2] = 1 + elif numerator == 6: + numerator_vec[0] = 1 + numerator_vec[3] = 1 + elif numerator == 9: + numerator_vec[1] = 1 + numerator_vec[3] = 1 + elif numerator == 12: + numerator_vec[0] = 1 + numerator_vec[2] = 1 + numerator_vec[3] = 1 + elif numerator == 24: + numerator_vec[0] = 1 + numerator_vec[2] = 1 + numerator_vec[3] = 1 + else: + print('Unclassified numerator: ', numerator) + numerator_vec[4] = 1 + + return numerator_vec + denominator_vec + + +def note_notation_to_vector(note): + """ Returns note notation vector + + Args: + note: Note() object in xml_notes + + Returns: + notation_vec (1-D list): multi-hot vector represents note notation in shape (num_keywords, ) + + Example: + (in feature_extraction.py -> ScoreExtractor().extract_score_features()) + >>> features['notation'] = [feature_utils.note_notation_to_vector( + note) for note in piece_data.xml_notes] + + """ + # trill, tenuto, accent, staccato, fermata + keywords = ['is_trill', 'is_tenuto', 'is_accent', 'is_staccato', + 'is_fermata', 'is_arpeggiate', 'is_strong_accent', 'is_cue', 'is_slash'] + # keywords = ['is_trill', 'is_tenuto', 'is_accent', 'is_staccato', 'is_fermata'] + + notation_vec = [0] * len(keywords) + + for i in range(len(keywords)): + key = keywords[i] + if getattr(note.note_notations, key) == True: + notation_vec[i] = 1 + + return notation_vec + + +def make_index_continuous(note_locations): + """ Returns continuous note location list + + Sometimes a beat or a measure can contain no notes at all. + In this case, the sequence of beat index or measure indices of notes are not continuous, + e.g. 0, 0, 0, 0, 1, 1, 1, 1, 4, 4, 4, 4 ... + This function ommits the beat or measure without any notes so that entire sequence of indices become continuous + + Args: + note_locations (1-D list) : list of .NoteLocation object + + Returns: + note_locations (1-D list) : list of .NoteLocation object + + Example: + (in feature_extraction.py -> ScoreExtractor().extract_score_features()) + >>> features['note_location'] = feature_utils.make_index_continuous(features['note_location']) + """ + prev_beat = 0 + prev_measure = 0 + + beat_compensate = 0 + measure_compensate = 0 + + for loc_data in note_locations: + if loc_data.beat - prev_beat > 1: + beat_compensate -= (loc_data.beat - prev_beat) - 1 + if loc_data.measure - prev_measure > 1: + measure_compensate -= (loc_data.measure - + prev_measure) - 1 + + prev_beat = loc_data.beat + prev_measure = loc_data.measure + + loc_data.beat += beat_compensate + loc_data.measure += measure_compensate + return note_locations + + +class NoteLocation: + def __init__(self, beat, measure, voice, section): + self.beat = beat + self.measure = measure + self.voice = voice + self.section = section + +class Tempo: + def __init__(self, xml_position, qpm, time_position, end_xml, end_time): + self.qpm = qpm + self.xml_position = xml_position + self.time_position = time_position + self.end_time = end_time + self.end_xml = end_xml + + def __str__(self): + string = '{From ' + str(self.xml_position) + string += ' to ' + str(self.end_xml) + return string + + +def cal_tempo_by_positions(beats, position_pairs): + """ Returns list of Tempo objects + + Args: + beats (1-D list): list of beats in piece + position_pairs (1-D list): list of valid pair dictionaries {xml_note, midi_note} + + Returns: + tempos (1-D list): list of .Tempo object + + Example: + (in feature_extraction.py -> PerformExtractor().get_beat_tempo()) + >>> tempos = feature_utils.cal_tempo_by_positions(piece_data.beat_positions, perform_data.valid_position_pairs) + + """ + tempos = [] + num_beats = len(beats) + previous_end = 0 + + for i in range(num_beats-1): + beat = beats[i] + current_pos_pair = utils.get_item_by_xml_position(position_pairs, beat) + if current_pos_pair['xml_position'] < previous_end: + continue + + next_beat = beats[i+1] + next_pos_pair = utils.get_item_by_xml_position(position_pairs, next_beat) + + if next_pos_pair['xml_position'] == previous_end: + continue + + if current_pos_pair == next_pos_pair: + continue + + cur_xml = current_pos_pair['xml_position'] + cur_time = current_pos_pair['time_position'] + cur_divisions = current_pos_pair['divisions'] + next_xml = next_pos_pair['xml_position'] + next_time = next_pos_pair['time_position'] + qpm = (next_xml - cur_xml) / (next_time - cur_time) / cur_divisions * 60 + + if qpm > 1000: + print('need check: qpm is ' + str(qpm) +', current xml_position is ' + str(cur_xml)) + tempo = Tempo(cur_xml, qpm, cur_time, next_xml, next_time) + tempos.append(tempo) # + previous_end = next_pos_pair['xml_position'] + + return tempos + + + +def pedal_sigmoid(pedal_value, k=8): + """ Returns + + Args: + pedal_value (integer) : pedal value in midi number + k (integer) + + Returns: + sigmoid_pedal (integer) : sigmoid pedal value + + Example: + (in feature_extraction.py -> PerformExtractor().get_pedal_at_start()) + >>> pedal = feature_utils.pedal_sigmoid(pair['midi'].pedal_at_start) + """ + sigmoid_pedal = 127 / (1 + math.exp(-(pedal_value-64)/k)) + return int(sigmoid_pedal) + + +def get_trill_parameters(): + return + + +def composer_name_to_vec(composer_name): + composer_name_list = ['Bach','Balakirev', 'Beethoven', 'Brahms', 'Chopin', 'Debussy', 'Glinka', 'Haydn', + 'Liszt', 'Mozart', 'Prokofiev', 'Rachmaninoff', 'Ravel', 'Schubert', 'Schumann', 'Scriabin'] + one_hot_vec = [0] * (len(composer_name_list) + 1) + if composer_name in composer_name_list: + index = composer_name_list.index(composer_name) + else: + index = len(composer_name_list) + print('The given composer name {} is not in the list'.format(composer_name)) + one_hot_vec[index] = 1 + + return one_hot_vec + + +def get_longer_level_dynamics(features, note_locations, length='beat'): + num_notes = len(note_locations) + + prev_beat = 0 + prev_beat_index = 0 + temp_beat_dynamic = [] + + longer_dynamics = [0] * num_notes + + for i in range(num_notes): + if not features['align_matched'][i]: + continue + current_beat = getattr(note_locations[i], length) + + if current_beat > prev_beat and temp_beat_dynamic != []: + prev_beat_dynamic = (sum(temp_beat_dynamic) / len(temp_beat_dynamic) + max(temp_beat_dynamic)) / 2 + for j in range(prev_beat_index, i): + longer_dynamics[j] = prev_beat_dynamic + temp_beat_dynamic = [] + prev_beat = current_beat + prev_beat_index = i + + temp_beat_dynamic.append(features['velocity'][i]) + + if temp_beat_dynamic != []: + prev_beat_dynamic = (sum(temp_beat_dynamic) + max(temp_beat_dynamic)) / 2 / len(temp_beat_dynamic) + + for j in range(prev_beat_index, num_notes): + longer_dynamics[j] = prev_beat_dynamic + + return longer_dynamics \ No newline at end of file diff --git a/midi_utils b/midi_utils deleted file mode 160000 index d88625d..0000000 --- a/midi_utils +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d88625d3c3d1f419954dbb44479ac35211ee3982 diff --git a/midi_utils/.gitignore b/midi_utils/.gitignore new file mode 100644 index 0000000..6937b31 --- /dev/null +++ b/midi_utils/.gitignore @@ -0,0 +1,16 @@ +etcs/* +.idea/* +*.pyc + +# virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +.Python +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +.venv +pip-selfcheck.json diff --git a/midi_utils/__init__.py b/midi_utils/__init__.py new file mode 100644 index 0000000..878841c --- /dev/null +++ b/midi_utils/__init__.py @@ -0,0 +1,3 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function diff --git a/midi_utils/copy_and_align.py b/midi_utils/copy_and_align.py new file mode 100644 index 0000000..415984d --- /dev/null +++ b/midi_utils/copy_and_align.py @@ -0,0 +1,87 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import shutil +import subprocess +from. import utils as utils +import argparse +import pretty_midi + +# find midi files in INPUT_DIR, and align it using Nakamura's alignment tool. +# (https://midialignment.github.io/demo.html) +# midi.mid in same subdirectory will be regarded as score file. +# make alignment result files in same directory. read Nakamura's manual for detail. + +INPUT_DIR = '/Users/Da/Documents/Github/musicXML-parser/chopin_cleaned/' + +parser = argparse.ArgumentParser() +parser.add_argument("--input_dir", default=INPUT_DIR, + help="Abs path to midi folder") +parser.add_argument("--align_dir", default='/home/ilcobo2/AlignmentTool_v2', + help="Abs path to Nakamura's Alignment tool") +args = parser.parse_args() +INPUT_DIR = args.input_dir + +os.chdir(args.align_dir) + +''' +# read from text list +f = open('temp_fix.txt', 'rb') +lines = f.readlines() +f.close() +midi_files = [el.strip() for el in lines] +''' + +# read from folder +midi_files = utils.find_files_in_subdir(INPUT_DIR, '*.mid') + +n_match = 0 +n_unmatch = 0 +for midi_file in midi_files: + + if 'midi.mid' in midi_file or 'XP.mid' in midi_file or 'midi_cleaned.mid' in midi_file: + continue + + if 'Chopin_Sonata' in midi_file: + continue + + if os.path.isfile(midi_file.replace('.mid', '_infer_corresp.txt')): + n_match += 1 + continue + + file_folder, file_name = utils.split_head_and_tail(midi_file) + perform_midi = midi_file + score_midi = os.path.join(file_folder, 'midi_cleaned.mid') + if not os.path.isfile(score_midi): + score_midi = os.path.join(file_folder, 'midi.mid') + print(perform_midi) + print(score_midi) + + mid = pretty_midi.PrettyMIDI(score_midi) + + n_notes = len(mid.instruments[0].notes) + + ''' + if n_notes >= 8000: + n_unmatch +=1 + continue + ''' + + shutil.copy(perform_midi, os.path.join(args.align_dir, 'infer.mid')) + shutil.copy(score_midi, os.path.join(args.align_dir, 'score.mid')) + + try: + subprocess.check_call(["sudo", "sh", "MIDIToMIDIAlign.sh", "score", "infer"]) + except: + print('Error to process {}'.format(midi_file)) + pass + else: + shutil.move('infer_corresp.txt', midi_file.replace('.mid', '_infer_corresp.txt')) + shutil.move('infer_match.txt', midi_file.replace('.mid', '_infer_match.txt')) + shutil.move('infer_spr.txt', midi_file.replace('.mid', '_infer_spr.txt')) + shutil.move('score_spr.txt', os.path.join(args.align_dir, '_score_spr.txt')) +print('match:{:d}, unmatch:{:d}'.format(n_match, n_unmatch)) + + diff --git a/midi_utils/copy_and_align_emotion.py b/midi_utils/copy_and_align_emotion.py new file mode 100644 index 0000000..0ffde5a --- /dev/null +++ b/midi_utils/copy_and_align_emotion.py @@ -0,0 +1,98 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import shutil +import subprocess +from. import utils as utils +import argparse +import pretty_midi + +# find midi files in INPUT_DIR, and align it using Nakamura's alignment tool. +# (https://midialignment.github.io/demo.html) +# midi.mid in same subdirectory will be regarded as score file. +# make alignment result files in same directory. read Nakamura's manual for detail. + +INPUT_DIR = '/Users/jeongdasaem/Documents/GitHub/virtuosoNet/pyScoreParser/EmotionData/' + +parser = argparse.ArgumentParser() +parser.add_argument("--input_dir", default=INPUT_DIR, + help="Abs path to midi folder") +parser.add_argument("--align_dir", default='/Users/jeongdasaem/Documents/AlignmentTool_v190813', + help="Abs path to Nakamura's Alignment tool") +args = parser.parse_args() +INPUT_DIR = args.input_dir + +os.chdir(args.align_dir) + +''' +# read from text list +f = open('temp_fix.txt', 'rb') +lines = f.readlines() +f.close() +midi_files = [el.strip() for el in lines] +''' + +# read from folder +midi_files = utils.find_files_in_subdir(INPUT_DIR, '*.mid') +xml_files = utils.find_files_in_subdir(INPUT_DIR, '*.musicxml') +score_file_name = {'piece': [], 'path': []} + +for xml_file in xml_files: + split_name = xml_file.split('/')[-1].split('.') + score_file_name['piece'].append('.'.join(split_name[1:4])) + score_file_name['path'].append(xml_file) + +print(score_file_name) + +n_match = 0 +n_unmatch = 0 +for midi_file in midi_files: + file_name = midi_file.split('/')[-1] + if file_name.split('.')[0][0].isdigit(): + continue + + if os.path.isfile(midi_file.replace('.mid', '_infer_corresp.txt')): + n_match += 1 + continue + + file_folder, file_name = utils.split_head_and_tail(midi_file) + perform_midi = midi_file + piece_name = '.'.join(file_name.split('.')[0:3]) + + if piece_name in score_file_name['piece']: + piece_index = score_file_name['piece'].index(piece_name) + else: + continue + score_midi = ".".join(score_file_name['path'][piece_index].split('.')[0:-1]) + ".mid" + + print(perform_midi) + print(score_midi) + + mid = pretty_midi.PrettyMIDI(score_midi) + + n_notes = len(mid.instruments[0].notes) + + ''' + if n_notes >= 8000: + n_unmatch +=1 + continue + ''' + + shutil.copy(perform_midi, os.path.join(args.align_dir, 'infer.mid')) + shutil.copy(score_midi, os.path.join(args.align_dir, 'score.mid')) + + try: + subprocess.check_call(["sudo", "sh", "MIDIToMIDIAlign.sh", "score", "infer"]) + except: + print('Error to process {}'.format(midi_file)) + pass + else: + shutil.move('infer_corresp.txt', midi_file.replace('.mid', '_infer_corresp.txt')) + shutil.move('infer_match.txt', midi_file.replace('.mid', '_infer_match.txt')) + shutil.move('infer_spr.txt', midi_file.replace('.mid', '_infer_spr.txt')) + shutil.move('score_spr.txt', os.path.join(args.align_dir, '_score_spr.txt')) +print('match:{:d}, unmatch:{:d}'.format(n_match, n_unmatch)) + + diff --git a/midi_utils/examples/MAPS_MUS-mz_545_3_ENSTDkCl.mid b/midi_utils/examples/MAPS_MUS-mz_545_3_ENSTDkCl.mid new file mode 100644 index 0000000..4b20060 Binary files /dev/null and b/midi_utils/examples/MAPS_MUS-mz_545_3_ENSTDkCl.mid differ diff --git a/midi_utils/examples/SMD_Chopin_Op010-03_007_20100611-SMD.mid b/midi_utils/examples/SMD_Chopin_Op010-03_007_20100611-SMD.mid new file mode 100644 index 0000000..ac96335 Binary files /dev/null and b/midi_utils/examples/SMD_Chopin_Op010-03_007_20100611-SMD.mid differ diff --git a/midi_utils/examples/Vienna_Chopin_op10_no3_p01.mid b/midi_utils/examples/Vienna_Chopin_op10_no3_p01.mid new file mode 100644 index 0000000..1460362 Binary files /dev/null and b/midi_utils/examples/Vienna_Chopin_op10_no3_p01.mid differ diff --git a/midi_utils/examples/Yamaha_chopin_10_3.mid b/midi_utils/examples/Yamaha_chopin_10_3.mid new file mode 100644 index 0000000..e964fd3 Binary files /dev/null and b/midi_utils/examples/Yamaha_chopin_10_3.mid differ diff --git a/midi_utils/midi_utils.py b/midi_utils/midi_utils.py new file mode 100644 index 0000000..d732e4f --- /dev/null +++ b/midi_utils/midi_utils.py @@ -0,0 +1,535 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import pretty_midi +import warnings +import numpy as np +import copy + + +ONSET_DURATION = 0.032 + + +class SustainPedal: + """A sustain_pedal event. + Parameters + ---------- + number : int + control number. {64, 127} + value : int + The value of the control change, in [0, 127]. + start, end : float or None + Time where the control change occurs. + """ + + def __init__(self, start, end, value, number): + self.number = number + self.value = value + self.start = start + self.end = end + + def __repr__(self): + return ('Sustain_Pedal (start={:f}, end={:f}, value={:d}, number={:d}' + .format(self.start, self.end, self.value, self.number)) + + def is_valid(self): + return self.end is not None and self.end > self.start + + + +def elongate_offset_by_pedal(midi_obj): + """elongate off set of notes in midi_object, according to sustain pedal length. + + :param + midi_obj: pretty_midi.PrettyMIDI object + :return: + pretty_midi.PrettyMIDI object + """ + + assert len(midi_obj.instruments) == 1 + pedals = read_sustain_pedal(midi_obj) + for pedal in pedals: + instrument = midi_obj.instruments[0] + for note in instrument.notes: + if pedal.start < note.end <= pedal.end: + note.end = pedal.end + + return midi_obj + +def add_pedal_inf_to_notes(midi_obj): + assert len(midi_obj.instruments) == 1 + sustain_pedals = read_sustain_pedal(midi_obj) + sustain_pedals_positions = [pedal.start for pedal in sustain_pedals] + soft_pedals = read_sustain_pedal(midi_obj,search_target=(67,)) # soft pedal CC == 67 + soft_pedals_positions = [pedal.start for pedal in soft_pedals] + sostenuto_pedals = read_sustain_pedal(midi_obj, search_target=(66,)) #sostenuto pedal CC == 66 + sostenuto_pedals_positions = [pedal.start for pedal in sostenuto_pedals] + + notes = midi_obj.instruments[0].notes + saved_notes = copy.copy(notes) + notes.sort(key=lambda note:note.start) + threshold = 30 + soft_threshold = 30 + # notes_offset_sorted = notes + # notes_offset_sorted.sort(key = lambda note: note.end) + + for note in notes: + pedal_index_at_note_start = binaryIndex(sustain_pedals_positions, note.start) + pedal_index_at_note_end = binaryIndex(sustain_pedals_positions, note.end) + soft_pedal_index = binaryIndex(soft_pedals_positions, note.start) + sostenuto_index_at_start = binaryIndex(sostenuto_pedals_positions, note.start) + sostenuto_index_at_end = binaryIndex(sostenuto_pedals_positions, note.end) + + note.pedal_at_start = sustain_pedals[pedal_index_at_note_start].value + note.pedal_at_end = sustain_pedals[pedal_index_at_note_end].value + note.soft_pedal = soft_pedals[soft_pedal_index].value + # note.sostenuto_at_start = sostenuto_pedals[sostenuto_index_at_start].value + # note.sostenuto_at_end = sostenuto_pedals[sostenuto_index_at_end].value + + note.pedal_refresh, note.pedal_refresh_time = \ + cal_pedal_refresh_in_note(note, notes, sustain_pedals,sustain_pedals_positions, + pedal_index_at_note_start, pedal_index_at_note_end) + # note.sostenuto_refresh = check_pedal_refresh_in_note(note, notes, sostenuto_pedals, sostenuto_pedals_positions, + # sostenuto_index_at_start, sostenuto_index_at_end) + + note.pedal_cut, note.pedal_cut_time =\ + cal_pedal_cut(note, notes, sustain_pedals, sustain_pedals_positions) + # note.sostenuto_cut = check_pedal_cut(note, notes, sostenuto_pedals, + # sostenuto_pedals_positions) + + new_notes = [0] * len(notes) + for note in notes: + old_index = saved_notes.index(note) + new_notes[old_index] = note + midi_obj.instruments[0].notes = new_notes + return midi_obj + +def cal_pedal_refresh_in_note(note, notes, pedals, pedals_positions, pd_ind1, pd_ind2): + note_index = notes.index(note) + search_time_end = note.end + lowest_pedal_value = note.pedal_at_start + lowest_pedal = None + # counts only when pedal is pressed at start + # if note.pedal_at_start == False: + # return False, 0 + + if note_index < len(notes) - 1: + next_note = notes[note_index+1] + if next_note.start < note.end: + pd_ind2 = binaryIndex(pedals_positions, next_note.start) + search_time_end = next_note.start + + # check only the pedal between note start and end + for i in range(pd_ind1, pd_ind2): + pedal = pedals[i] + if pedal.value < lowest_pedal_value: + lowest_pedal_value = pedal.value + lowest_pedal = pedal + if lowest_pedal: + time_ratio = (lowest_pedal.start - note.start) / (note.end - note.start) + return lowest_pedal_value, time_ratio + else: + return lowest_pedal_value, 0 + +def cal_pedal_cut(note, notes, pedals, pedals_positions, threshold=30): + note_index = notes.index(note) + lowest_pedal_value = note.pedal_at_start + lowest_pedal = None + if note_index == 0: + return 0, 0 + #check that there is no activated notes when the note starts + prev_notes = notes[0:note_index] + prev_notes.sort(key=lambda note:note.end) + index = -1 + while index-1 >= -note_index and prev_notes[index].end >= note.start: + index += -1 + # if last_note.end > note.start: + # return False, 0 + + last_note = prev_notes[index] + + + pd1 = binaryIndex(pedals_positions, last_note.end) + pd2 = binaryIndex(pedals_positions, note.start) + for i in range(pd1, pd2): + pedal = pedals[i] + if pedal.value < lowest_pedal_value: + lowest_pedal_value = pedal.value + lowest_pedal = pedal + + if lowest_pedal: + time_ratio = (note.start - lowest_pedal.start) / (note.end - note.start) + return lowest_pedal_value, time_ratio + else: + return lowest_pedal_value, 0 + +def add_pedal_inf_to_notes(midi_obj): + assert len(midi_obj.instruments) == 1 + sustain_pedals = read_sustain_pedal(midi_obj) + sustain_pedals_positions = [pedal.start for pedal in sustain_pedals] + soft_pedals = read_sustain_pedal(midi_obj,search_target=(67,)) # soft pedal CC == 67 + soft_pedals_positions = [pedal.start for pedal in soft_pedals] + sostenuto_pedals = read_sustain_pedal(midi_obj, search_target=(66,)) #sostenuto pedal CC == 66 + sostenuto_pedals_positions = [pedal.start for pedal in sostenuto_pedals] + + notes = midi_obj.instruments[0].notes + saved_notes = copy.copy(notes) + notes.sort(key=lambda note:note.start) + threshold = 30 + soft_threshold = 30 + # notes_offset_sorted = notes + # notes_offset_sorted.sort(key = lambda note: note.end) + + for note in notes: + pedal_index_at_note_start = binaryIndex(sustain_pedals_positions, note.start) + pedal_index_at_note_end = binaryIndex(sustain_pedals_positions, note.end) + soft_pedal_index = binaryIndex(soft_pedals_positions, note.start) + sostenuto_index_at_start = binaryIndex(sostenuto_pedals_positions, note.start) + sostenuto_index_at_end = binaryIndex(sostenuto_pedals_positions, note.end) + + note.pedal_at_start = sustain_pedals[pedal_index_at_note_start].value + note.pedal_at_end = sustain_pedals[pedal_index_at_note_end].value + note.soft_pedal = soft_pedals[soft_pedal_index].value + # note.sostenuto_at_start = sostenuto_pedals[sostenuto_index_at_start].value + # note.sostenuto_at_end = sostenuto_pedals[sostenuto_index_at_end].value + + note.pedal_refresh, note.pedal_refresh_time = \ + cal_pedal_refresh_in_note(note, notes, sustain_pedals,sustain_pedals_positions, + pedal_index_at_note_start, pedal_index_at_note_end) + # note.sostenuto_refresh = check_pedal_refresh_in_note(note, notes, sostenuto_pedals, sostenuto_pedals_positions, + # sostenuto_index_at_start, sostenuto_index_at_end) + + note.pedal_cut, note.pedal_cut_time = \ + cal_pedal_cut_after(note, notes, sustain_pedals, sustain_pedals_positions) + # note.sostenuto_cut = check_pedal_cut(note, notes, sostenuto_pedals, + # sostenuto_pedals_positions) + + # new_notes = [0] * len(notes) + # for note in notes: + # old_index = saved_notes.index(note) + # new_notes[old_index] = note + # midi_obj.instruments[0].notes = new_notes + return midi_obj + +def cal_pedal_refresh_in_note(note, notes, pedals, pedals_positions, pd_ind1, pd_ind2): + note_index = notes.index(note) + search_time_end = note.end + lowest_pedal_value = note.pedal_at_start + lowest_pedal = None + # counts only when pedal is pressed at start + # if note.pedal_at_start == False: + # return False, 0 + + # if note_index < len(notes) - 1: + # next_note = notes[note_index+1] + # if next_note.start < note.end: + # pd_ind2 = binaryIndex(pedals_positions, next_note.start) + # search_time_end = next_note.start + + # check only the pedal between note start and end + for i in range(pd_ind1, pd_ind2): + pedal = pedals[i] + if pedal.value < lowest_pedal_value: + lowest_pedal_value = pedal.value + lowest_pedal = pedal + if lowest_pedal: + time_ratio = (lowest_pedal.start - note.start) #/ (note.end - note.start) + return lowest_pedal_value, time_ratio + else: + return lowest_pedal_value, 0 + + +def cal_pedal_cut(note, notes, pedals, pedals_positions, threshold=30): + note_index = notes.index(note) + lowest_pedal_value = note.pedal_at_start + lowest_pedal = None + if note_index == 0: + return 0, 0 + #check that there is no activated notes when the note starts + prev_notes = notes[0:note_index] + prev_notes.sort(key=lambda note:note.end) + index = -1 + while index-1 >= -note_index and prev_notes[index].end >= note.start: + index += -1 + # if last_note.end > note.start: + # return False, 0 + + last_note = prev_notes[index] + + pd1 = binaryIndex(pedals_positions, last_note.end) + pd2 = binaryIndex(pedals_positions, note.start) + for i in range(pd1, pd2): + pedal = pedals[i] + if pedal.value < lowest_pedal_value: + lowest_pedal_value = pedal.value + lowest_pedal = pedal + notes.sort(key=lambda x:x.start) + if lowest_pedal: + time_ratio = (note.start - lowest_pedal.start) / (note.end - note.start) + return lowest_pedal_value, time_ratio + else: + return lowest_pedal_value, 0 + + +def cal_pedal_cut_after(note, notes, pedals, pedals_positions, threshold=30): + note_index = notes.index(note) + note_end = note.end + lowest_pedal_value = note.pedal_at_end + lowest_pedal = None + if note_index == 0: + return 0, 0 + #check that there is no activated notes when the note starts + next_notes = notes[note_index+1:] + next_onset = float('Inf') + for nxt_nt in next_notes: + if nxt_nt.start > note_end: + next_onset = nxt_nt.start + break + # if last_note.end > note.start: + # return False, 0 + pd1 = binaryIndex(pedals_positions, note.end) + pd2 = binaryIndex(pedals_positions, next_onset) + + for i in range(pd1, pd2): + pedal = pedals[i] + if pedal.value < lowest_pedal_value: + lowest_pedal_value = pedal.value + lowest_pedal = pedal + notes.sort(key=lambda x:x.start) + if lowest_pedal: + time_ratio = (lowest_pedal.start - note_end) + return lowest_pedal_value, time_ratio + else: + return lowest_pedal_value, 0 + +def to_midi_zero(midi_path, midi_min=21, midi_max=108, save_midi=False, save_name=None): + """Convert midi files to midi-0 format (1 track). set resolution = 1000, tempo=120. + + :param + midi_path: path to .mid file + midi_min: minimum midi number to convert. belows will be ignored. + midi_max: maximum midi number to convert. highers will be ignored. + save_midi: if true, save midi with name *.midi0.mid + save_name: full path of save file. if given, save midi file with given name. + :return: + 0-type pretty_midi.Pretty_Midi object. + """ + pretty_midi.pretty_midi.MAX_TICK = 1e10 + if save_name is None: + save_name = midi_path.replace('.mid', '_midi0.mid') + + midi = pretty_midi.PrettyMIDI(midi_path) + midi_new = pretty_midi.PrettyMIDI(resolution=1000, initial_tempo=120) + instrument = pretty_midi.Instrument(0) + for instruments in midi.instruments: + for midi_note in instruments.notes: + note_pitch = midi_note.pitch + if midi_min <= note_pitch <= midi_max: + instrument.notes.append(midi_note) + else: + print('note with pitch : {:d} detected. Omitted because note not in [{:d}, {:d}]' + .format(note_pitch, midi_min, midi_max)) + for controls in instruments.control_changes: + instrument.control_changes.append(controls) + midi_new.instruments.append(instrument) + midi_new.remove_invalid_notes() + if save_midi: + midi_new.write(save_name) + return midi_new + + +def read_sustain_pedal(midi_obj, threshold=0, search_target=(64, 127)): + """Read sustain pedal in midi. + + :param + midi_obj: pretty_midi.Pretty_Midi object. + threshold: threshold of velocity to activate/deactivate pedal + :return: + list of SustainPedal objects + """ + assert len(midi_obj.instruments) == 1 + instrument = midi_obj.instruments[0] + pedals = [] + default_pedal = SustainPedal(0,0.0001,0,search_target[0]) + pedals.append(default_pedal) # always starts with zero pedal + current_pedal = None + for control in instrument.control_changes: + # 64 is allocated for sustain pedal, but MAPS uses 127 as pedal + if control.number in search_target: + if control.value > threshold: + if isinstance(current_pedal, SustainPedal): + current_pedal.end = control.time + pedals.append(current_pedal) + current_pedal = SustainPedal(control.time, None, control.value, control.number) + elif control.value <= threshold: + if isinstance(current_pedal, SustainPedal): + current_pedal.end = control.time + pedals.append(current_pedal) + current_pedal = None + else: + warnings.warn('Sustain pedal offset detected without onset. Omitted') + if isinstance(current_pedal, SustainPedal): + warnings.warn('Last Sustain pedal detected without offset. Add offset at end') + current_pedal.end = midi_obj.get_end_time() + pedals.append(current_pedal) + return pedals + + +def mid2piano_roll(midi_path, pedal=False, onset=False, midi_min=21, midi_max=108, clean_midi=True, fps=50.0): + """Convert midi into piano-roll like array + + :param + midi_path: midi path + pedal: if True, elongate offset according to pedal + onset: if True, mark only onset frame + midi_min: minimum midi number to convert. belows will be ignored. + midi_max: maximum midi number to convert. highers will be ignored. + clean_midi: if True, clean up midi file before process. + fps: frame rate per second. accept float values(ex: 36.6) + :return: + numpy array of piano roll, (midi_num, time_frames) + """ + assert (pedal and onset) is not True, 'pedal + onset is not reasonable' + + if clean_midi: + mid = to_midi_zero(midi_path, midi_min, midi_max) + else: + mid = pretty_midi.PrettyMIDI(midi_path) + if pedal: + mid = elongate_offset_by_pedal(mid) + + max_step = int(np.ceil(mid.get_end_time() * fps)) + dim = midi_max - midi_min + 1 + + roll = np.zeros((max_step, dim)) + + def time_to_frame(start, end): + start_frame = int(start * fps) + end_frame = int(end * fps) + return start_frame, end_frame + + if onset: + for note in mid.instruments[0].notes: + start_time = note.start + end_time = np.min([start_time + ONSET_DURATION, note.end]) + start_frame, end_frame = time_to_frame(start_time, end_time) + roll[start_frame: end_frame, note.pitch - midi_min] = 1 + else: + for note in mid.instruments[0].notes: + start_time = note.start + end_time = note.end + start_frame, end_frame = time_to_frame(start_time, end_time) + roll[start_frame: end_frame, note.pitch - midi_min] = 1 + + return roll + + +def piano_roll2chroma_roll(piano_roll): + """Convert piano roll into chroma roll + # TODO: fixed indexing, according to midi_min + :param + piano_roll: numpy array of shape (midi_num, time_frames) + :return: + chroma roll, numpy array of shape (12, time_frames) + + """ + + chroma_roll = np.zeros((piano_roll.shape[0], 12)) # (time, class) + for n in range(piano_roll.shape[1]): + chroma_roll[:, n % 12] += piano_roll[:, n] + chroma_roll = (chroma_roll >= 1).astype(np.int) + return chroma_roll + + +def mid2chroma_roll(midi_path, pedal=False, onset=False): + piano_roll = mid2piano_roll(midi_path, pedal=pedal, onset=onset) + chroma_roll = piano_roll2chroma_roll(piano_roll) + return chroma_roll + + +def binaryIndex(alist, item): + first = 0 + last = len(alist)-1 + midpoint = 0 + + if(item< alist[first]): + return 0 + + while first item: + return midpoint + else: first = midpoint +1; + if first == last and alist[last] > item: + return midpoint + elif currentElement > item: + last = midpoint -1 + else: + if midpoint +1 ==len(alist): + return midpoint + while alist[midpoint+1] == item: + midpoint += 1 + if midpoint + 1 == len(alist): + return midpoint + return midpoint + return last + + +def save_note_pedal_to_CC(midi_obj, bool_pedal=False, disklavier=False): + # input = pretty midi object with pedal inf embedded in note (e.g. note.pedal_at_start etc.) + assert len(midi_obj.instruments) == 1 + instrument = midi_obj.instruments[0] + notes = instrument.notes + notes.sort(key=lambda note:note.start) + num_notes = len(notes) + eps = 0.03 # hyper-parameter + + def to_8(value): + # if value == True: + # return 127 + # else: + # return 0 + return int(min(max(value,0),127)) + + primary_pedal = [] + secondary_pedal = [] + for i in range(num_notes): + note = notes[i] + next_note = notes[min(i+1, num_notes-1)] + # print(note.start, note.end, note.pitch, note.pedal_refresh, note.pedal_cut) + pedal1 = pretty_midi.ControlChange(number=64, value=to_8(note.pedal_at_start), time=note.start-eps) + pedal2 = pretty_midi.ControlChange(number=64, value=to_8(note.pedal_at_end), time=note.end-eps) + soft_pedal = pretty_midi.ControlChange(number=67, value=to_8(note.soft_pedal), time=note.start-eps) + + instrument.control_changes.append(pedal1) + instrument.control_changes.append(pedal2) + instrument.control_changes.append(soft_pedal) + primary_pedal.append(pedal1) + primary_pedal.append(pedal2) + + # if note.pedal_refresh: + refresh_time = note.start + note.pedal_refresh_time #(note.end - note.start) * note.pedal_refresh_time + pedal3 = pretty_midi.ControlChange(number=64, value=to_8(note.pedal_refresh), time=refresh_time) + if pedal3.time < pedal2.time: + secondary_pedal.append(pedal3) + instrument.control_changes.append(pedal3) + # + # if note.pedal_cut: + # cut_time = note.start - (note.end - note.start) * note.pedal_cut_time + cut_time = note.end + note.pedal_cut_time + pedal4 = pretty_midi.ControlChange(number=64, value=to_8(note.pedal_cut), time=cut_time) + instrument.control_changes.append(pedal4) + secondary_pedal.append(pedal4) + primary_pedal.sort(key=lambda x: x.time) + + + last_note_end = notes[-1].end + # end pedal 3 seconds after the last note + last_pedal = pretty_midi.ControlChange(number=64, value=0, time=last_note_end + 3) + instrument.control_changes.append(last_pedal) + + return midi_obj \ No newline at end of file diff --git a/midi_utils/readme.md b/midi_utils/readme.md new file mode 100644 index 0000000..9a9783c --- /dev/null +++ b/midi_utils/readme.md @@ -0,0 +1,29 @@ +# MIDI Utils +* Python2.x + +## Development Environment Setup +1. Update pip. +``` +pip install --upgrade pip +``` + +2. Create a virtual environment. +``` +python -m venv [name] +``` + +3. Activate a virtual environment. +* On Windows, run: +``` +[name]\Scripts\activate.bat +``` + +* On Unix or MacOS, run: +``` +source [name]/bin/activate +``` + +4. Install third party packages. +``` +pip install -r requirements.txt +``` diff --git a/midi_utils/requirements.txt b/midi_utils/requirements.txt new file mode 100644 index 0000000..04f021a --- /dev/null +++ b/midi_utils/requirements.txt @@ -0,0 +1,4 @@ +mido==1.2.8 +numpy==1.14.3 +pretty-midi==0.2.8 +six==1.11.0 diff --git a/midi_utils/test.ipynb b/midi_utils/test.ipynb new file mode 100644 index 0000000..de343f3 --- /dev/null +++ b/midi_utils/test.ipynb @@ -0,0 +1,261 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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\nSZIkab3/DxxxocffPItlAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from __future__ import absolute_import\n", + "from __future__ import division\n", + "from __future__ import print_function\n", + "\n", + "import midi_utils\n", + "import mido\n", + "import pretty_midi\n", + "import visualize_utils\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "\n", + "# MIDI_FILE = \"examples/MAPS_MUS-mz_545_3_ENSTDkCl.mid\"\n", + "# MIDI_FILE = \"examples/Yamaha_chopin_10_3.mid\"\n", + "# MIDI_FILE = \"examples/SMD_Chopin_Op010-03_007_20100611-SMD.mid\"\n", + "MIDI_FILE = \"examples/Vienna_Chopin_op10_no3_p01.mid\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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\nSZIkab3/DxxxocffPItlAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def tick2time(tick, tempo, ticks_per_beat):\n", + " # convert midi tick to seconds.\n", + " return tick / ticks_per_beat * tempo / 1e6\n", + "\n", + "print('Midi file: {}\\n'.format(MIDI_FILE))\n", + "\n", + "# -------Parse with mido (Low level)--------\n", + "midi_data = mido.MidiFile(MIDI_FILE)\n", + "print('parse with MIDO:\\n {}\\n'.format(midi_data))\n", + "\n", + "print('Length: {}\\n'.format(midi_data.length))\n", + "\n", + "print('n_tracks: {:d}\\n'.format(len(midi_data.tracks)))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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\nSZIkab3/DxxxocffPItlAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print('Messages(events) in first track:')\n", + "for idx, msg in enumerate(midi_data.tracks[0]):\n", + " print('{:d}: {}'.format(idx, msg))\n", + "\n", + "print('\\nMessages(events) in second track (first 10)')\n", + "for idx, msg in enumerate(midi_data.tracks[1]):\n", + " if idx > 10:\n", + " break\n", + " print('{:d}: {}'.format(idx, msg))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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\nSZIkab3/DxxxocffPItlAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print('\\nConvert event time in second track into time(sec)')\n", + "\n", + "try:\n", + " tempo = midi_data.tracks[0][0].tempo # there can be multiple tempo messages, in different position\n", + "except:\n", + " tempo = 5e5 # default value\n", + "ticks_per_beat = midi_data.ticks_per_beat\n", + "\n", + "print('Tempo: {:d}, Ticks_per_beats: {:d}. 1000 ticks ~= {:0.4f} seconds\\n'.\n", + " format(tempo, ticks_per_beat, tick2time(1000, tempo, ticks_per_beat)))\n", + "\n", + "time_in_sec = 0\n", + "for idx, msg in enumerate(midi_data.tracks[1]):\n", + " if idx > 10:\n", + " break\n", + " time_in_sec += tick2time(msg.time, tempo, ticks_per_beat)\n", + " print('{:d}: {}, sec: {:0.4f}'.format(idx, msg, time_in_sec))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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\nSZIkab3/DxxxocffPItlAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# -------Parse with pretty_midi (high level)--------\n", + "midi_data = pretty_midi.PrettyMIDI(MIDI_FILE)\n", + "print('parse with Pretty_midi:\\n {}\\n'.format(midi_data))\n", + "\n", + "print('Length: {}'.format(midi_data.get_end_time()))\n", + "print('Length difference comes from omitting End of Tracks message\\n')\n", + "\n", + "print('n_tracks: {:d}'.format(len(midi_data.instruments)))\n", + "print('Pretty midi merges events from multiple tracks. checkout the docs for details\\n')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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\nSZIkab3/DxxxocffPItlAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print('Notes in first instruments:')\n", + "for idx, msg in enumerate(midi_data.instruments[0].notes):\n", + " if idx > 10:\n", + " break\n", + " print('{:d}: {}'.format(idx, msg))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "image/png": "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\nSZIkab3/DxxxocffPItlAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# -------Draw piano roll------\n", + "\n", + "fps = 10\n", + "piano_roll = midi_utils.mid2piano_roll(MIDI_FILE, fps=fps)\n", + "plt.figure(figsize=(20, 10))\n", + "visualize_utils.draw_piano_roll(piano_roll, draw_range=[0, 300], fps=fps)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/midi_utils/test.py b/midi_utils/test.py new file mode 100644 index 0000000..ef8b72a --- /dev/null +++ b/midi_utils/test.py @@ -0,0 +1,81 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import midi_utils +import mido +import pretty_midi +import visualize_utils + + +# MIDI_FILE = "examples/MAPS_MUS-mz_545_3_ENSTDkCl.mid" +# MIDI_FILE = "examples/Yamaha_chopin_10_3.mid" +# MIDI_FILE = "examples/SMD_Chopin_Op010-03_007_20100611-SMD.mid" +MIDI_FILE = "examples/Vienna_Chopin_op10_no3_p01.mid" + + +def tick2time(tick, tempo, ticks_per_beat): + # convert midi tick to seconds. + return tick / ticks_per_beat * tempo / 1e6 + + +if __name__ == "__main__": + print('Midi file: {}\n'.format(MIDI_FILE)) + + # -------Parse with mido (Low level)-------- + midi_data = mido.MidiFile(MIDI_FILE) + print('parse with MIDO:\n {}\n'.format(midi_data)) + + print('Length: {}\n'.format(midi_data.length)) + + print('n_tracks: {:d}\n'.format(len(midi_data.tracks))) + + print('Messages(events) in first track:') + for idx, msg in enumerate(midi_data.tracks[0]): + print('{:d}: {}'.format(idx, msg)) + + print('\nMessages(events) in second track (first 10)') + for idx, msg in enumerate(midi_data.tracks[1]): + if idx > 10: + break + print('{:d}: {}'.format(idx, msg)) + + print('\nConvert event time in second track into time(sec)') + + try: + tempo = midi_data.tracks[0][0].tempo # there can be multiple tempo messages, in different position + except: + tempo = 5e5 # default value + ticks_per_beat = midi_data.ticks_per_beat + + print('Tempo: {:d}, Ticks_per_beats: {:d}. 1000 ticks ~= {:0.4f} seconds'. + format(tempo, ticks_per_beat, tick2time(1000, tempo, ticks_per_beat))) + + time_in_sec = 0 + for idx, msg in enumerate(midi_data.tracks[1]): + if idx > 10: + break + time_in_sec += tick2time(msg.time, tempo, ticks_per_beat) + print('{:d}: {}, sec: {:0.4f}'.format(idx, msg, time_in_sec)) + + # -------Parse with pretty_midi (high level)-------- + midi_data = pretty_midi.PrettyMIDI(MIDI_FILE) + print('parse with Pretty_midi:\n {}\n'.format(midi_data)) + + print('Length: {}'.format(midi_data.get_end_time())) + print('Length difference comes from omitting End of Tracks message\n') + + print('n_tracks: {:d}'.format(len(midi_data.instruments))) + print('Pretty midi merges events from multiple tracks. checkout the docs for details\n') + + print('Notes in first instruments:') + for idx, msg in enumerate(midi_data.instruments[0].notes): + if idx > 10: + break + print('{:d}: {}'.format(idx, msg)) + + # -------Draw piano roll------ + + fps = 10 + piano_roll = midi_utils.mid2piano_roll(MIDI_FILE, fps=fps) + visualize_utils.draw_piano_roll(piano_roll, draw_range=[0, 300], fps=fps) diff --git a/midi_utils/utils.py b/midi_utils/utils.py new file mode 100644 index 0000000..ad82f57 --- /dev/null +++ b/midi_utils/utils.py @@ -0,0 +1,125 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import json +import pickle +import ntpath +import fnmatch + + +def save_dict_as_json(config): + if not os.path.exists(config.save_dir): + os.makedirs(config.save_dir) + param_path = os.path.join(config.save_dir, "params.json") + with open(param_path, 'w') as fp: + json.dump(config.__dict__, fp, indent=4, sort_keys=True) + + +def maybe_make_dir(dir_name): + if not os.path.isdir(dir_name): + os.makedirs(dir_name) + + +def change_name_extension(file_name, new_extension): + if '.' not in new_extension: + new_extension = '.' + new_extension + base = os.path.splitext(file_name)[0] + return base + new_extension + + +def split_head_and_tail(file_path): + head, tail = ntpath.split(file_path) + return head, tail + + +def save_obj(obj, name): + save_name = change_name_extension(name, '.pkl') + with open(save_name, 'wb') as f: + pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) + + +def load_obj(name): + save_name = change_name_extension(name, '.pkl') + with open(save_name, 'rb') as f: + return pickle.load(f) + + +def find_files_in_subdir(folder, regexp): + match_files = [] + for root, subdir, base in os.walk(folder): + for file_name in fnmatch.filter(base, regexp): + match_files.append(os.path.join(root, file_name)) + return match_files + + +''' +import numpy as np +import math +from numpy.lib.stride_tricks import as_strided + +def array2stack(array, length, hop=None): + if hop is None: + hop = length + assert (array.shape[0] - length) % hop == 0, 'length of array is not fit. l={:d}, length={:d}, hop={:d}' \ + .format(array.shape[0], length, hop) + strides = array.strides + stack = as_strided(array, ((array.shape[0] - length) // hop + 1, length, array.shape[1]), + (strides[0] * hop, strides[0], strides[1])) + return stack + + +def overlap_stack2array(stack): + # TODO: what if hop != stack.shape[1]//2 ? + hop = stack.shape[1] // 2 + length = (stack.shape[0] + 1) * hop + array = np.zeros((length, stack.shape[2])) + array[:hop // 2, :] = stack[0, :hop // 2, :] + for n in xrange(stack.shape[0]): + array[n * hop + hop // 2: n * hop + 3 * hop // 2, :] = stack[n, hop // 2: 3 * hop // 2, :] + array[(stack.shape[0] - 1) * hop + 3 * hop // 2:, :] = stack[stack.shape[0] - 1, 3 * hop // 2:, :] + + return array + + +def onset2delayed(onset, delay_len=10): + rolled_onset = np.zeros(onset.shape) + for k in range(delay_len): + temp = np.roll(onset, k, axis=0) + temp[0, :] = 0 + weight = math.sqrt((delay_len - k) / float(delay_len)) + rolled_onset += temp * weight + rolled_onset[rolled_onset > 1] = 1 + return rolled_onset + + +def record_as_text(config, text): + if not os.path.exists(config.save_dir): + os.makedirs(config.save_dir) + record_txt = config.save_dir + '/' + 'summary.txt' + f = open(record_txt, 'a') + f.write(text) + f.close() + + +def get_data_list(set_name, set_num=1): + f = open('data_list/config{:d}_{}.txt'.format(set_num, set_name), 'rb') + data_list = f.readlines() + for n in xrange(len(data_list)): + data_list[n] = data_list[n].replace('\n', '') + f.close() + data_list.sort() + return data_list + + +def pad2d(feature, seg_len): + if feature.shape[0] % seg_len != 0: + pad_len = seg_len - feature.shape[0] % seg_len + feature = np.pad(feature, ((0, pad_len), (0, 0)), 'constant') + return feature + + +def normalize(feature, mean, std): + return np.divide((feature - mean[None, :]), std[None, :], where=(std[None, :] != 0)) +''' \ No newline at end of file diff --git a/midi_utils/visualize_utils.py b/midi_utils/visualize_utils.py new file mode 100644 index 0000000..0eec955 --- /dev/null +++ b/midi_utils/visualize_utils.py @@ -0,0 +1,49 @@ +import numpy as np +import matplotlib.pyplot as plt + + +def my_imshow(array, interpolation='nearest', origin='bottom', aspect='auto', cmap='gray', **kwargs): + plt.imshow(array, interpolation=interpolation, origin=origin, aspect=aspect, cmap=cmap, **kwargs) + + +note_names = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'] + + +def midi_name(midi_num): + octave = str(midi_num // 12 - 1) + note = note_names[(midi_num + 3) % 12] + return octave, note + + +def draw_piano_roll(roll, draw_range=None, fps=10, midi_min=21, midi_max=108): + if not draw_range: + draw_range = [roll.shape[0], roll.shape[1]] + plt.imshow(roll[draw_range[0]: draw_range[1], :].T, interpolation='nearest', aspect='auto', + origin='lower', cmap=plt.get_cmap('gray_r')) + tick_range = range((draw_range[1] - draw_range[0]) // fps) + + # draw guide lines (octave line) + number = 12 + cmap = plt.get_cmap('Paired') + colors = [cmap(i) for i in np.linspace(0, 1, number)] + + n_midi = midi_max - midi_min + 1 + edge = range(12 - midi_min % 12, n_midi, 12) + for el in edge: + plt.plot(draw_range, [el, el], color='red', linewidth=1, linestyle="--", alpha=0.8) + midi_ticks = [midi_name(el)[1] + midi_name(el)[0] for el in range(midi_min, midi_max)] + ''' + # only shows white notes names + for n in range(len(midi_ticks)): + if '#' in midi_ticks[n]: + midi_ticks[n] = '' + ''' + + for n in range(n_midi): + plt.plot(draw_range, [n, n], color=colors[n % 12], linewidth=7, linestyle="-", alpha=0.07) + plt.yticks(range(n_midi), midi_ticks) + plt.ylim([0 - 0.5, n_midi - 0.5]) + + plt.xticks([el * fps for el in tick_range], [el + draw_range[0] for el in tick_range]) + plt.xlabel('time(sec)') + plt.xlim(draw_range) \ No newline at end of file diff --git a/musicxml_parser b/musicxml_parser deleted file mode 160000 index db27849..0000000 --- a/musicxml_parser +++ /dev/null @@ -1 +0,0 @@ -Subproject commit db27849292af3bbb4e153cc097b56593162b5756 diff --git a/musicxml_parser/.gitignore b/musicxml_parser/.gitignore new file mode 100644 index 0000000..50cda9f --- /dev/null +++ b/musicxml_parser/.gitignore @@ -0,0 +1,110 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +.idea/ +*$py.class + +# dat files +*.dat + + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + diff --git a/musicxml_parser/README.md b/musicxml_parser/README.md new file mode 100644 index 0000000..5993212 --- /dev/null +++ b/musicxml_parser/README.md @@ -0,0 +1 @@ +# musicXML-parser \ No newline at end of file diff --git a/musicxml_parser/__init__.py b/musicxml_parser/__init__.py new file mode 100644 index 0000000..168110b --- /dev/null +++ b/musicxml_parser/__init__.py @@ -0,0 +1 @@ +from .mxp import MusicXMLDocument \ No newline at end of file diff --git a/musicxml_parser/mxp/__init__.py b/musicxml_parser/mxp/__init__.py new file mode 100644 index 0000000..9d3562b --- /dev/null +++ b/musicxml_parser/mxp/__init__.py @@ -0,0 +1,19 @@ +# Copyright 2018. +# Music and Audio Research Group, Seoul National University http://marg.snu.ac.kr/ +# Music and Audio Computing Lab, KAIST http://mac.kaist.ac.kr/ +# All Rights Reserved. + +# Our inspiration for the project came from Google Magenta project, +# putting the "music" in artificial intelligence. +# In magenta, it has simple MusicXML parser used to convert MusicXML +# into tensorflow.magenta.NoteSequence. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 + + +"""Imports objects from music modules into the top-level music namespace.""" + +from .main import MusicXMLDocument diff --git a/musicxml_parser/mxp/chord_symbol.py b/musicxml_parser/mxp/chord_symbol.py new file mode 100644 index 0000000..40168ba --- /dev/null +++ b/musicxml_parser/mxp/chord_symbol.py @@ -0,0 +1,275 @@ +from . import constants +from .exception import ChordSymbolParseException + + +class ChordSymbol(object): + """Internal representation of a MusicXML chord symbol element. + + This represents a chord symbol with four components: + + 1) Root: a string representing the chord root pitch class, e.g. "C#". + 2) Kind: a string representing the chord kind, e.g. "m7" for minor-seventh, + "9" for dominant-ninth, or the empty string for major triad. + 3) Scale degree modifications: a list of strings representing scale degree + modifications for the chord, e.g. "add9" to add an unaltered ninth scale + degree (without the seventh), "b5" to flatten the fifth scale degree, + "no3" to remove the third scale degree, etc. + 4) Bass: a string representing the chord bass pitch class, or None if the bass + pitch class is the same as the root pitch class. + + There's also a special chord kind "N.C." representing no harmony, for which + all other fields should be None. + + Use the `get_figure_string` method to get a string representation of the chord + symbol as might appear in a lead sheet. This string representation is what we + use to represent chord symbols in NoteSequence protos, as text annotations. + While the MusicXML representation has more structure, using an unstructured + string provides more flexibility and allows us to ingest chords from other + sources, e.g. guitar tabs on the web. + """ + + # The below dictionary maps chord kinds to an abbreviated string as would + # appear in a chord symbol in a standard lead sheet. There are often multiple + # standard abbreviations for the same chord type, e.g. "+" and "aug" both + # refer to an augmented chord, and "maj7", "M7", and a Delta character all + # refer to a major-seventh chord; this dictionary attempts to be consistent + # but the choice of abbreviation is somewhat arbitrary. + # + # The MusicXML-defined chord kinds are listed here: + # http://usermanuals.musicxml.com/MusicXML/Content/ST-MusicXML-kind-value.htm + + CHORD_KIND_ABBREVIATIONS = { + # These chord kinds are in the MusicXML spec. + 'major': '', + 'minor': 'm', + 'augmented': 'aug', + 'diminished': 'dim', + 'dominant': '7', + 'major-seventh': 'maj7', + 'minor-seventh': 'm7', + 'diminished-seventh': 'dim7', + 'augmented-seventh': 'aug7', + 'half-diminished': 'm7b5', + 'major-minor': 'm(maj7)', + 'major-sixth': '6', + 'minor-sixth': 'm6', + 'dominant-ninth': '9', + 'major-ninth': 'maj9', + 'minor-ninth': 'm9', + 'dominant-11th': '11', + 'major-11th': 'maj11', + 'minor-11th': 'm11', + 'dominant-13th': '13', + 'major-13th': 'maj13', + 'minor-13th': 'm13', + 'suspended-second': 'sus2', + 'suspended-fourth': 'sus', + 'pedal': 'ped', + 'power': '5', + 'none': 'N.C.', + + # These are not in the spec, but show up frequently in the wild. + 'dominant-seventh': '7', + 'augmented-ninth': 'aug9', + 'minor-major': 'm(maj7)', + + # Some abbreviated kinds also show up frequently in the wild. + '': '', + 'min': 'm', + 'aug': 'aug', + 'dim': 'dim', + '7': '7', + 'maj7': 'maj7', + 'min7': 'm7', + 'dim7': 'dim7', + 'm7b5': 'm7b5', + 'minMaj7': 'm(maj7)', + '6': '6', + 'min6': 'm6', + 'maj69': '6(add9)', + '9': '9', + 'maj9': 'maj9', + 'min9': 'm9', + 'sus47': 'sus7' + } + + def __init__(self, xml_harmony, state): + self.xml_harmony = xml_harmony + self.time_position = -1 + self.xml_position = -1 + self.root = None + self.kind = '' + self.degrees = [] + self.bass = None + self.state = state + self._parse() + + def _alter_to_string(self, alter_text): + """Parse alter text to a string of one or two sharps/flats. + + Args: + alter_text: A string representation of an integer number of semitones. + + Returns: + A string, one of 'bb', 'b', '#', '##', or the empty string. + + Raises: + ChordSymbolParseException: If `alter_text` cannot be parsed to an integer, + or if the integer is not a valid number of semitones between -2 and 2 + inclusive. + """ + # Parse alter text to an integer number of semitones. + try: + alter_semitones = int(alter_text) + except ValueError: + raise ChordSymbolParseException('Non-integer alter: ' + str(alter_text)) + + # Visual alter representation + if alter_semitones == -2: + alter_string = 'bb' + elif alter_semitones == -1: + alter_string = 'b' + elif alter_semitones == 0: + alter_string = '' + elif alter_semitones == 1: + alter_string = '#' + elif alter_semitones == 2: + alter_string = '##' + else: + raise ChordSymbolParseException('Invalid alter: ' + str(alter_semitones)) + + return alter_string + + def _parse(self): + """Parse the MusicXML element.""" + self.time_position = self.state.time_position + self.xml_position = self.state.xml_position + for child in self.xml_harmony: + if child.tag == 'root': + self._parse_root(child) + elif child.tag == 'kind': + if child.text is None: + # Seems like this shouldn't happen but frequently does in the wild... + continue + kind_text = str(child.text).strip() + if kind_text not in self.CHORD_KIND_ABBREVIATIONS: + raise ChordSymbolParseException('Unknown chord kind: ' + kind_text) + self.kind = self.CHORD_KIND_ABBREVIATIONS[kind_text] + elif child.tag == 'degree': + self.degrees.append(self._parse_degree(child)) + elif child.tag == 'bass': + self._parse_bass(child) + elif child.tag == 'offset': + # Offset tag moves chord symbol time position. + try: + offset = int(child.text) + except ValueError: + raise ChordSymbolParseException('Non-integer offset: ' + + str(child.text)) + midi_ticks = offset * constants.STANDARD_PPQ / self.state.divisions + seconds = (midi_ticks / constants.STANDARD_PPQ * + self.state.seconds_per_quarter) + self.time_position += seconds + self.xml_position += offset + else: + # Ignore other tag types because they are not relevant to mxp. + pass + + if self.root is None and self.kind != 'N.C.': + raise ChordSymbolParseException('Chord symbol must have a root') + + def _parse_pitch(self, xml_pitch, step_tag, alter_tag): + """Parse and return the pitch-like or element.""" + if xml_pitch.find(step_tag) is None: + raise ChordSymbolParseException('Missing pitch step') + step = xml_pitch.find(step_tag).text + + alter_string = '' + if xml_pitch.find(alter_tag) is not None: + alter_text = xml_pitch.find(alter_tag).text + alter_string = self._alter_to_string(alter_text) + + if self.state.transpose: + raise ChordSymbolParseException( + 'Transposition of chord symbols currently unsupported') + + return step + alter_string + + def _parse_root(self, xml_root): + """Parse the tag for a chord symbol.""" + self.root = self._parse_pitch(xml_root, step_tag='root-step', + alter_tag='root-alter') + + def _parse_bass(self, xml_bass): + """Parse the tag for a chord symbol.""" + self.bass = self._parse_pitch(xml_bass, step_tag='bass-step', + alter_tag='bass-alter') + + def _parse_degree(self, xml_degree): + """Parse and return the scale degree modification element.""" + if xml_degree.find('degree-value') is None: + raise ChordSymbolParseException('Missing scale degree value in harmony') + value_text = xml_degree.find('degree-value').text + if value_text is None: + raise ChordSymbolParseException('Missing scale degree') + try: + value = int(value_text) + except ValueError: + raise ChordSymbolParseException('Non-integer scale degree: ' + + str(value_text)) + + alter_string = '' + if xml_degree.find('degree-alter') is not None: + alter_text = xml_degree.find('degree-alter').text + alter_string = self._alter_to_string(alter_text) + + if xml_degree.find('degree-type') is None: + raise ChordSymbolParseException('Missing degree modification type') + type_text = xml_degree.find('degree-type').text + + if type_text == 'add': + if not alter_string: + # When adding unaltered scale degree, use "add" string. + type_string = 'add' + else: + # When adding altered scale degree, "add" not necessary. + type_string = '' + elif type_text == 'subtract': + type_string = 'no' + # Alter should be irrelevant when removing scale degree. + alter_string = '' + elif type_text == 'alter': + if not alter_string: + raise ChordSymbolParseException('Degree alteration by zero semitones') + # No type string necessary as merely appending e.g. "#9" suffices. + type_string = '' + else: + raise ChordSymbolParseException('Invalid degree modification type: ' + + str(type_text)) + + # Return a scale degree modification string that can be appended to a chord + # symbol figure string. + return type_string + alter_string + str(value) + + def __str__(self): + if self.kind == 'N.C.': + note_string = '{kind: ' + self.kind + '} ' + else: + note_string = '{root: ' + self.root + note_string += ', kind: ' + self.kind + note_string += ', degrees: [%s]' % ', '.join(degree + for degree in self.degrees) + note_string += ', bass: ' + self.bass + '} ' + note_string += '(@time: ' + str(self.time_position) + ')' + return note_string + + def get_figure_string(self): + """Return a chord symbol figure string.""" + if self.kind == 'N.C.': + return self.kind + else: + degrees_string = ''.join('(%s)' % degree for degree in self.degrees) + figure = self.root + self.kind + degrees_string + if self.bass: + figure += '/' + self.bass + return figure diff --git a/musicxml_parser/mxp/constants.py b/musicxml_parser/mxp/constants.py new file mode 100644 index 0000000..dc25074 --- /dev/null +++ b/musicxml_parser/mxp/constants.py @@ -0,0 +1,77 @@ +"""Constants for music processing""" + +# Meter-related constants. +DEFAULT_QUARTERS_PER_MINUTE = 120.0 +DEFAULT_STEPS_PER_BAR = 16 # 4/4 music sampled at 4 steps per quarter note. +DEFAULT_STEPS_PER_QUARTER = 4 + +# Default absolute quantization. +DEFAULT_STEPS_PER_SECOND = 100 + +# Standard pulses per quarter. +# https://en.wikipedia.org/wiki/Pulses_per_quarter_note +STANDARD_PPQ = 220 + +# Special melody events. +NUM_SPECIAL_MELODY_EVENTS = 2 +MELODY_NOTE_OFF = -1 +MELODY_NO_EVENT = -2 + +# Other melody-related constants. +MIN_MELODY_EVENT = -2 +MAX_MELODY_EVENT = 127 +MIN_MIDI_PITCH = 0 # Inclusive. +MAX_MIDI_PITCH = 127 # Inclusive. +NOTES_PER_OCTAVE = 12 + +# Velocity-related constants. +MIN_MIDI_VELOCITY = 1 # Inclusive. +MAX_MIDI_VELOCITY = 127 # Inclusive. + +# Program-related constants. +MIN_MIDI_PROGRAM = 0 +MAX_MIDI_PROGRAM = 127 + +# Chord symbol for "no chord". +NO_CHORD = 'N.C.' + +# The indices of the pitch classes in a major scale. +MAJOR_SCALE = [0, 2, 4, 5, 7, 9, 11] + +# NOTE_KEYS[note] = The major keys that note belongs to. +# ex. NOTE_KEYS[0] lists all the major keys that contain the note C, +# which are: +# [0, 1, 3, 5, 7, 8, 10] +# [C, C#, D#, F, G, G#, A#] +# +# 0 = C +# 1 = C# +# 2 = D +# 3 = D# +# 4 = E +# 5 = F +# 6 = F# +# 7 = G +# 8 = G# +# 9 = A +# 10 = A# +# 11 = B +# +# NOTE_KEYS can be generated using the code below, but is explicitly declared +# for readability: +# NOTE_KEYS = [[j for j in range(12) if (i - j) % 12 in MAJOR_SCALE] +# for i in range(12)] +NOTE_KEYS = [ + [0, 1, 3, 5, 7, 8, 10], + [1, 2, 4, 6, 8, 9, 11], + [0, 2, 3, 5, 7, 9, 10], + [1, 3, 4, 6, 8, 10, 11], + [0, 2, 4, 5, 7, 9, 11], + [0, 1, 3, 5, 6, 8, 10], + [1, 2, 4, 6, 7, 9, 11], + [0, 2, 3, 5, 7, 8, 10], + [1, 3, 4, 6, 8, 9, 11], + [0, 2, 4, 5, 7, 9, 10], + [1, 3, 5, 6, 8, 10, 11], + [0, 2, 4, 6, 7, 9, 11] +] diff --git a/musicxml_parser/mxp/direction.py b/musicxml_parser/mxp/direction.py new file mode 100644 index 0000000..3a94ec7 --- /dev/null +++ b/musicxml_parser/mxp/direction.py @@ -0,0 +1,168 @@ +import copy + +class Direction(object): + """Internal representation of a MusicXML Measure's Direction properties. + + This represents musical dynamic symbols, expressions with six components: + 1) dynamic # 'ppp', 'pp', 'p', 'mp' 'mf', 'f', 'ff' 'fff + 2) pedal # 'start' or 'stop' or 'change' 'continue' or None + 3) tempo # integer + 4) wedge # 'crescendo' or 'diminuendo' or 'stop' or None + 5) words # string e.g) Andantino + 6) velocity # integer + 7) octave-shift + 8) metronome + + It parses the standard of the marking point of note. + """ + def __init__(self, xml_direction, state): + self.xml_direction = xml_direction + self.type = {'type': None, 'content': None} + self.state = copy.copy(state) + self.placement = None + self.staff = None + self.time_position = state.time_position + self.xml_position = state.xml_position + self._parse() + + + def _parse(self): + """Parse the MusicXML element.""" + direction = self.xml_direction + child_list = direction.find('direction-type').getchildren() + if len(child_list) == 0: + return + staff = direction.find('staff') + if staff is not None: + self.staff = staff.text + if 'placement' in direction.attrib.keys(): + self.placement = direction.attrib['placement'] + for child in child_list: + if child is not None: + if child.tag == "dynamics": + self._parse_dynamics(child) + elif child.tag == "pedal": + self._parse_pedal(child) + elif child.tag == "wedge": + self._parse_wedge(child) + elif child.tag == "words" or child.tag=="other-dynamics": + self._parse_words(child) + elif child.tag=='octave-shift': + self._parse_octave_shift(child) + elif child.tag=='metronome': + self._parse_metronome(child) + + + def _parse_pedal(self, xml_pedal): + """Parse the MusicXML element. + + Args: + xml_pedal: XML element with tag type 'pedal'. + """ + pedal = xml_pedal.attrib['type'] + self.type = {'type': 'pedal', 'content': pedal} + + def _parse_sound(self, xml_direction): + """Parse the MusicXML element. + + Args: + xml_direction: XML element with tag type 'direction'. + """ + sound_tag = xml_direction.find('sound') + if sound_tag is not None: + attrib = sound_tag.attrib + if 'dynamics' in attrib: + velocity = attrib['dynamics'] + self.type = {'type':'velocity', 'content': velocity} + + elif 'tempo' in attrib: + tempo = attrib['tempo'] + self.type = {'type':'tempo', 'content': tempo} + + def _parse_dynamics(self, xml_dynamics): + """Parse the MusicXML element. + + Args: + xml_dynamics: XML element with tag type 'dynamics'. + """ + dynamic = xml_dynamics.getchildren()[0].tag + if dynamic == 'other-dynamics': + content = xml_dynamics.getchildren()[0].text + if content: + while 'dynamicPiano' in content: + content = content.replace('dynamicPiano', 'p') + while 'dynamicForte' in content: + content = content.replace('dynamicForte', 'f') + while 'dynamicMezzo' in content: + content = content.replace('dynamicMezzo', 'm') + while 'dynamicSforzando' in content: + content = content.replace('dynamicSforzando', 'sf') + while 'dynamicRinforzando' in content: + content = content.replace('dynamicRinforzando', 'r') + while 'dynamicNiente' in content: + content = content.replace('dynamicNiente', 'n') + while 'dynamicZ' in content: + content = content.replace('dynamicZ', 'z') + if content is not None: + self.type = {'type':'words', 'content': content} + else: + self.type = {'type':'dynamic', 'content': dynamic} + + def _parse_wedge(self, xml_wedge): + """Parse the MusicXML element. + + Args: + xml_wedge: XML element with tag type 'wedge'. + """ + wedge_type_labels = ['crescendo', 'diminuendo'] + wedge_type = xml_wedge.attrib['type'] + if 'number' in xml_wedge.attrib.keys(): + wedge_index = xml_wedge.attrib['number'] + else: + wedge_index = None + + if wedge_type in wedge_type_labels: + # Add "start" at the point of a wedge starting point + self.type = {'type':wedge_type, 'content': 'start', 'number': wedge_index} + + else: + # if wedge_type == 'stop': + # if self.state.previous_direction.type['type'] is not None: + # previous_type = list(self.state.previous_direction.type['type'])[0] + # + # if previous_type in wedge_type_labels: + # self.type = {'type':previous_type, 'content': wedge_type, 'number': wedge_index} + # else: + self.type = {'type':'none', 'content': wedge_type, 'number': wedge_index} + + def _parse_words(self, xml_words): + """Parse the MusicXML element. + + Args: + xml_wedge: XML element with tag type 'wedge'. + """ + # self.type = {'type':'words', 'content': xml_words.text.decode('utf-8')} + if self.type['content'] is None: + self.type = {'type': 'words', 'content': xml_words.text} + else: + self.type['content'] += xml_words.text + + def _parse_octave_shift(self, xml_shift): + """Parse the MusicXML element. + + """ + self.type = {'type': 'octave-shift', 'content': xml_shift.attrib['type'], 'size': xml_shift.attrib['size']} + + def _parse_metronome(self, xml_metronome): + """Parse the MusicXML element. + + """ + self.type = {'type':'metronome', 'content': xml_metronome.find('per-minute'), 'beat-unit': xml_metronome.find('beat-unit')} + + def __str__(self): + direction_string = '{type: ' + str(self.type['type']) + ' - ' + str(self.type['content'].encode('utf-8')) + direction_string += ', xml_position: ' + str(self.xml_position) + direction_string += ', staff: ' + str(self.staff) + '}' + return direction_string + + diff --git a/musicxml_parser/mxp/direction_constants.py b/musicxml_parser/mxp/direction_constants.py new file mode 100644 index 0000000..8a8b044 --- /dev/null +++ b/musicxml_parser/mxp/direction_constants.py @@ -0,0 +1,25 @@ +"""Internal representation of a MusicXML Measure's Direction properties. + + This represents musical dynamic symbols, expressions with six components: + 1) dynamic # 'ppp', 'pp', 'p', 'mp' 'mf', 'f', 'ff' 'fff + 2) pedal # 'start' or 'stop' or 'change' 'continue' or None + 3) tempo # integer + 4) wedge # 'crescendo' or 'diminuendo' or 'stop' or None + 5) words # string e.g) Andantino + 6) velocity # integer + 7) octave-shift + 8) metronome + + +""" + +DIRECTION_TYPES = [ + 'dynamic', + 'pedal', + 'tempo', + 'wedge', + 'words', + 'velocity', + 'octave-shift', + 'metronome' +] diff --git a/musicxml_parser/mxp/exception.py b/musicxml_parser/mxp/exception.py new file mode 100644 index 0000000..348b0aa --- /dev/null +++ b/musicxml_parser/mxp/exception.py @@ -0,0 +1,59 @@ +import six + +DEFAULT_MIDI_PROGRAM = 0 # Default MIDI Program (0 = grand piano) +DEFAULT_MIDI_CHANNEL = 0 # Default MIDI Channel (0 = first channel) +MUSICXML_MIME_TYPE = 'application/vnd.recordare.musicxml+xml' + + +class MusicXMLParseException(Exception): + """Exception thrown when the MusicXML contents cannot be parsed.""" + pass + + +class PitchStepParseException(MusicXMLParseException): + """Exception thrown when a pitch step cannot be parsed. + + Will happen if pitch step is not one of A, B, C, D, E, F, or G + """ + pass + + +class ChordSymbolParseException(MusicXMLParseException): + """Exception thrown when a chord symbol cannot be parsed.""" + pass + + +class MultipleTimeSignatureException(MusicXMLParseException): + """Exception thrown when multiple time signatures found in a measure.""" + pass + + +class AlternatingTimeSignatureException(MusicXMLParseException): + """Exception thrown when an alternating time signature is encountered.""" + pass + + +class TimeSignatureParseException(MusicXMLParseException): + """Exception thrown when the time signature could not be parsed.""" + pass + + +class UnpitchedNoteException(MusicXMLParseException): + """Exception thrown when an unpitched note is encountered. + + We do not currently support parsing files with unpitched notes (e.g., + percussion scores). + + http://www.musicxml.com/tutorial/percussion/unpitched-notes/ + """ + pass + + +class KeyParseException(MusicXMLParseException): + """Exception thrown when a key signature cannot be parsed.""" + pass + + +class InvalidNoteDurationTypeException(MusicXMLParseException): + """Exception thrown when a note's duration type is invalid.""" + pass diff --git a/musicxml_parser/mxp/key_signature.py b/musicxml_parser/mxp/key_signature.py new file mode 100644 index 0000000..10f3395 --- /dev/null +++ b/musicxml_parser/mxp/key_signature.py @@ -0,0 +1,55 @@ +from .exception import KeyParseException +import copy + + +class KeySignature(object): + """Internal representation of a MusicXML key signature.""" + + def __init__(self, state, xml_key=None): + self.xml_key = xml_key + # MIDI and MusicXML identify key by using "fifths": + # -1 = F, 0 = C, 1 = G etc. + self.key = 0 + # mode is "major" or "minor" only: MIDI only supports major and minor + self.mode = 'major' + self.time_position = -1 + self.xml_position = -1 + self.state = copy.copy(state) + if xml_key is not None: + self._parse() + + def _parse(self): + """Parse the MusicXML element into a MIDI compatible key. + + If the mode is not minor (e.g. dorian), default to "major" + because MIDI only supports major and minor modes. + + + Raises: + KeyParseException: If the fifths element is missing. + """ + fifths = self.xml_key.find('fifths') + if fifths is None: + raise KeyParseException( + 'Could not find fifths attribute in key signature.') + self.key = int(self.xml_key.find('fifths').text) + mode = self.xml_key.find('mode') + # Anything not minor will be interpreted as major + if mode != 'minor': + mode = 'major' + self.mode = mode + self.time_position = self.state.time_position + self.xml_position = self.state.xml_position + + def __str__(self): + keys = (['Cb', 'Gb', 'Db', 'Ab', 'Eb', 'Bb', 'F', 'C', 'G', 'D', + 'A', 'E', 'B', 'F#', 'C#']) + key_string = keys[self.key + 7] + ' ' + self.mode + key_string += ' (@time: ' + str(self.time_position) + ')' + return key_string + + def __eq__(self, other): + isequal = self.key == other.key + isequal = isequal and (self.mode == other.mode) + isequal = isequal and (self.time_position == other.time_position) + return isequal diff --git a/musicxml_parser/mxp/main.py b/musicxml_parser/mxp/main.py new file mode 100644 index 0000000..566ccc5 --- /dev/null +++ b/musicxml_parser/mxp/main.py @@ -0,0 +1,613 @@ +"""MusicXML parser. +""" + +# Imports +# Python 2 uses integer division for integers. Using this gives the Python 3 +# behavior of producing a float when dividing integers +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from fractions import Fraction +import xml.etree.ElementTree as ET +import zipfile +import math +from .exception import MusicXMLParseException, MultipleTimeSignatureException + +# internal imports + +import six +from . import constants + +from .measure import Measure +from .tempo import Tempo +from .key_signature import KeySignature +from .score_part import ScorePart +from .part import Part +from .playable_notes import get_playable_notes + +DEFAULT_MIDI_PROGRAM = 0 # Default MIDI Program (0 = grand piano) +DEFAULT_MIDI_CHANNEL = 0 # Default MIDI Channel (0 = first channel) +MUSICXML_MIME_TYPE = 'application/vnd.recordare.musicxml+xml' + + +class MusicXMLParserState(object): + """Maintains internal state of the MusicXML parser.""" + + def __init__(self): + # Default to one division per measure + # From the MusicXML documentation: "The divisions element indicates + # how many divisions per quarter note are used to indicate a note's + # duration. For example, if duration = 1 and divisions = 2, + # this is an eighth note duration." + self.divisions = 1 + + # Default to a tempo of 120 quarter notes per minute + # MusicXML calls this tempo, but mxp calls this qpm + # Therefore, the variable is called qpm, but reads the + # MusicXML tempo attribute + # (120 qpm is the default tempo according to the + # Standard MIDI Files 1.0 Specification) + self.qpm = 120 + + # Duration of a single quarter note in seconds + self.seconds_per_quarter = 0.5 + + # Running total of time for the current event in seconds. + # Resets to 0 on every part. Affected by and elements + self.time_position = 0 + self.xml_position = 0 + + # Default to a MIDI velocity of 64 (mf) + self.velocity = 64 + + # Default MIDI program (0 = grand piano) + self.midi_program = DEFAULT_MIDI_PROGRAM + + # Current MIDI channel (usually equal to the part number) + self.midi_channel = DEFAULT_MIDI_CHANNEL + + # Keep track of previous note to get chord timing correct + # This variable stores an instance of the Note class (defined below) + self.previous_note_duration = 0 + self.previous_note_time_position = 0 + self.previous_note_xml_position = 0 + + # Keep track of previous direction + # self.previous_direction = None + + # Keep track of current transposition level in +/- semitones. + self.transpose = 0 + + # Keep track of current time signature. Does not support polymeter. + self.time_signature = None + + # Keep track of previous (unsolved) grace notes + self.previous_grace_notes = [] + + # Keep track of chord index + self.chord_index = 0 + + # Keep track of measure number + self.measure_number = 0 + + # Keep track of unsolved ending bracket + self.first_ending_discontinue = False + + # Keep track of beam status + self.is_beam_start = False + self.is_beam_continue = False + self.is_beam_stop = False + + + +class MusicXMLDocument(object): + """Internal representation of a MusicXML Document. + + Represents the top level object which holds the MusicXML document + Responsible for loading the .xml or .mxl file using the _get_score method + If the file is .mxl, this class uncompresses it + + After the file is loaded, this class then parses the document into memory + using the parse method. + """ + + def __init__(self, filename): + self._score = self._get_score(filename) + self.parts = [] + # ScoreParts indexed by id. + self._score_parts = {} + self.midi_resolution = constants.STANDARD_PPQ + self._state = MusicXMLParserState() + # Total time in seconds + self.total_time_secs = 0 + self.total_time_duration = 0 + self._parse() + self._recalculate_time_position() + + @staticmethod + def _get_score(filename): + """Given a MusicXML file, return the score as an xml.etree.ElementTree. + + Given a MusicXML file, return the score as an xml.etree.ElementTree + If the file is compress (ends in .mxl), uncompress it first + + Args: + filename: The path of a MusicXML file + + Returns: + The score as an xml.etree.ElementTree. + + Raises: + MusicXMLParseException: if the file cannot be parsed. + """ + score = None + if filename.endswith('.mxl'): + # Compressed MXL file. Uncompress in memory. + try: + mxlzip = zipfile.ZipFile(filename) + except zipfile.BadZipfile as exception: + raise MusicXMLParseException(exception) + + # A compressed MXL file may contain multiple files, but only one + # MusicXML file. Read the META-INF/container.xml file inside of the + # MXL file to locate the MusicXML file within the MXL file + # http://www.musicxml.com/tutorial/compressed-mxl-files/zip-archive-structure/ + + # Raise a MusicXMLParseException if multiple MusicXML files found + + infolist = mxlzip.infolist() + if six.PY3: + # In py3, instead of returning raw bytes, ZipFile.infolist() tries to + # guess the filenames' encoding based on file headers, and decodes using + # this encoding in order to return a list of strings. If the utf-8 + # header is missing, it decodes using the DOS code page 437 encoding + # which is almost definitely wrong. Here we need to explicitly check + # for when this has occurred and change the encoding to utf-8. + # https://stackoverflow.com/questions/37723505/namelist-from-zipfile-returns-strings-with-an-invalid-encoding + zip_filename_utf8_flag = 0x800 + for info in infolist: + if info.flag_bits & zip_filename_utf8_flag == 0: + filename_bytes = info.filename.encode('437') + filename = filename_bytes.decode('utf-8', 'replace') + info.filename = filename + + container_file = [x for x in infolist + if x.filename == 'META-INF/container.xml'] + compressed_file_name = '' + + if container_file: + try: + container = ET.fromstring(mxlzip.read(container_file[0])) + for rootfile_tag in container.findall('./rootfiles/rootfile'): + if 'media-type' in rootfile_tag.attrib: + if rootfile_tag.attrib['media-type'] == MUSICXML_MIME_TYPE: + if not compressed_file_name: + compressed_file_name = rootfile_tag.attrib['full-path'] + else: + raise MusicXMLParseException( + 'Multiple MusicXML files found in compressed archive') + else: + # No media-type attribute, so assume this is the MusicXML file + if not compressed_file_name: + compressed_file_name = rootfile_tag.attrib['full-path'] + else: + raise MusicXMLParseException( + 'Multiple MusicXML files found in compressed archive') + except ET.ParseError as exception: + raise MusicXMLParseException(exception) + + if not compressed_file_name: + raise MusicXMLParseException( + 'Unable to locate main .xml file in compressed archive.') + if six.PY2: + # In py2, the filenames in infolist are utf-8 encoded, so + # we encode the compressed_file_name as well in order to + # be able to lookup compressed_file_info below. + compressed_file_name = compressed_file_name.encode('utf-8') + try: + compressed_file_info = [x for x in infolist + if x.filename == compressed_file_name][0] + except IndexError: + raise MusicXMLParseException( + 'Score file %s not found in zip archive' % compressed_file_name) + score_string = mxlzip.read(compressed_file_info) + try: + score = ET.fromstring(score_string) + except ET.ParseError as exception: + raise MusicXMLParseException(exception) + else: + # Uncompressed XML file. + try: + tree = ET.parse(filename) + score = tree.getroot() + except ET.ParseError as exception: + raise MusicXMLParseException(exception) + + return score + + def _parse(self): + """Parse the uncompressed MusicXML document.""" + # Parse part-list + xml_part_list = self._score.find('part-list') + if xml_part_list is not None: + for element in xml_part_list: + if element.tag == 'score-part': + score_part = ScorePart(element) + self._score_parts[score_part.id] = score_part + + # Parse parts + for score_part_index, child in enumerate(self._score.findall('part')): + part = Part(child, self._score_parts, self._state) + self.parts.append(part) + score_part_index += 1 + if self._state.time_position > self.total_time_secs: + self.total_time_secs = self._state.time_position + if self._state.xml_position > self.total_time_duration: + self.total_time_duration = self._state.xml_position + + def _recalculate_time_position(self): + """ Sometimes, the tempo marking is not located in the first voice. + Therefore, the time position of each object should be calculate after parsing the entire tempo objects. + + """ + tempos = self.get_tempos() + + tempos.sort(key=lambda x: x.xml_position) + if tempos[0].xml_position != 0: + default_tempo = Tempo(self._state) + default_tempo.xml_position = 0 + default_tempo.time_position = 0 + default_tempo.qpm = constants.DEFAULT_QUARTERS_PER_MINUTE + default_tempo.state.divisions = tempos[0].state.divisions + tempos.insert(0, default_tempo) + new_time_position = 0 + for i in range(len(tempos)): + tempos[i].time_position = new_time_position + if i + 1 < len(tempos): + new_time_position += (tempos[i + 1].xml_position - tempos[i].xml_position) / tempos[i].qpm * 60 / tempos[ + i].state.divisions + + for part in self.parts: + for measure in part.measures: + for note in measure.notes: + for i in range(len(tempos)): + if i + 1 == len(tempos): + current_tempo = tempos[i].qpm / 60 * tempos[i].state.divisions + break + else: + if tempos[i].xml_position <= note.note_duration.xml_position and tempos[ + i + 1].xml_position > note.note_duration.xml_position: + current_tempo = tempos[i].qpm / 60 * tempos[i].state.divisions + break + note.note_duration.time_position = tempos[i].time_position + ( + note.note_duration.xml_position - tempos[i].xml_position) / current_tempo + note.note_duration.seconds = note.note_duration.duration / current_tempo + + def get_chord_symbols(self): + """Return a list of all the chord symbols used in this score.""" + chord_symbols = [] + for part in self.parts: + for measure in part.measures: + for chord_symbol in measure.chord_symbols: + if chord_symbol not in chord_symbols: + # Prevent duplicate chord symbols + chord_symbols.append(chord_symbol) + return chord_symbols + + def get_time_signatures(self): + """Return a list of all the time signatures used in this score. + + Does not support polymeter (i.e. assumes all parts have the same + time signature, such as Part 1 having a time signature of 6/8 + while Part 2 has a simultaneous time signature of 2/4). + + Ignores duplicate time signatures to prevent mxp duplicate + time signature error. This happens when multiple parts have the + same time signature is used in multiple parts at the same time. + + Example: If Part 1 has a time siganture of 4/4 and Part 2 also + has a time signature of 4/4, then only instance of 4/4 is sent + to mxp. + + Returns: + A list of all TimeSignature objects used in this score. + """ + time_signatures = [] + for part in self.parts: + for measure in part.measures: + if measure.time_signature is not None: + if measure.time_signature not in time_signatures: + # Prevent duplicate time signatures + time_signatures.append(measure.time_signature) + + return time_signatures + + def get_key_signatures(self): + """Return a list of all the key signatures used in this score. + + Support different key signatures in different parts (score in + written pitch). + + Ignores duplicate key signatures to prevent mxp duplicate key + signature error. This happens when multiple parts have the same + key signature at the same time. + + Example: If the score is in written pitch and the + flute is written in the key of Bb major, the trombone will also be + written in the key of Bb major. However, the clarinet and trumpet + will be written in the key of C major because they are Bb transposing + instruments. + + If no key signatures are found, create a default key signature of + C major. + + Returns: + A list of all KeySignature objects used in this score. + """ + key_signatures = [] + for part in self.parts: + for measure in part.measures: + if measure.key_signature is not None: + if measure.key_signature not in key_signatures: + # Prevent duplicate key signatures + key_signatures.append(measure.key_signature) + + if not key_signatures: + # If there are no key signatures, add C major at the beginning + key_signature = KeySignature(self._state) + key_signature.time_position = 0 + key_signature.xml_position = 0 + key_signatures.append(key_signature) + + return key_signatures + + def get_tempos(self): + """Return a list of all tempos in this score. + + If no tempos are found, create a default tempo of 120 qpm. + + Returns: + A list of all Tempo objects used in this score. + """ + tempos = [] + + if self.parts: + part = self.parts[0] # Use only first part + for measure in part.measures: + for tempo in measure.tempos: + tempos.append(tempo) + + # If no tempos, add a default of 120 at beginning + if not tempos: + tempo = Tempo(self._state) + tempo.qpm = self._state.qpm + tempo.time_position = 0 + tempo.xml_position = 0 + tempos.append(tempo) + + return tempos + + def recalculate_time_position(self): + tempos = self.get_tempos() + + tempos.sort(key=lambda x: x.xml_position) + new_time_position = 0 + for i in range(len(tempos)): + tempos[i].time_position = new_time_position + if i + 1 < len(tempos): + new_time_position += (tempos[i + 1].xml_position - tempos[i].xml_position) / tempos[i].qpm * 60 / tempos[ + i].state.divisions + + for part in self.parts: + for measure in part.measures: + for note in measure.notes: + for i in range(len(tempos)): + if i + 1 == len(tempos): + current_tempo = tempos[i].qpm / 60 * tempos[i].state.divisions + break + else: + if tempos[i].xml_position <= note.note_duration.xml_position and tempos[ + i + 1].xml_position > note.note_duration.xml_position: + current_tempo = tempos[i].qpm / 60 * tempos[i].state.divisions + break + note.note_duration.time_position = tempos[i].time_position + ( + note.note_duration.xml_position - tempos[i].xml_position) / current_tempo + note.note_duration.seconds = note.note_duration.duration / current_tempo + def get_measure_positions(self): + part = self.parts[0] + measure_positions = [] + + for measure in part.measures: + measure_positions.append(measure.start_xml_position) + + return measure_positions + + + def get_notes(self, melody_only=False, grace_note=True): + notes = [] + rests = [] + num_parts = len(self.parts) + for instrument_index in range(num_parts): + part = self.parts[instrument_index] + + notes_part, rests_part = get_playable_notes(part) + notes.extend(notes_part) + rests.extend(rests_part) + + return notes, rests + + + def find(self, f, seq): + items_list = [] + for item in seq: + if f(item): + items_list.append(item) + return items_list + + def rearrange_chord_index(self, xml_notes): + # assert all(xml_notes[i].pitch[1] >= xml_notes[i + 1].pitch[1] for i in range(len(xml_notes) - 1) + # if xml_notes[i].note_duration.xml_position ==xml_notes[i+1].note_duration.xml_position) + + previous_position = [-1] + max_chord_index = [0] + for note in xml_notes: + voice = note.voice - 1 + while voice >= len(previous_position): + previous_position.append(-1) + max_chord_index.append(0) + if note.note_duration.is_grace_note: + continue + if note.staff == 1: + if note.note_duration.xml_position > previous_position[voice]: + previous_position[voice] = note.note_duration.xml_position + max_chord_index[voice] = note.chord_index + note.chord_index = 0 + else: + note.chord_index = (max_chord_index[voice] - note.chord_index) + else: # note staff ==2 + pass + + return xml_notes + + def get_directions(self): + directions = [] + for part in self.parts: + for measure in part.measures: + for direction in measure.directions: + directions.append(direction) + + directions.sort(key=lambda x: x.xml_position) + cleaned_direction = [] + for i in range(len(directions)): + dir = directions[i] + if not dir.type == None: + if dir.type['type'] == "none": + for j in range(i): + prev_dir = directions[i-j-1] + if 'number' in prev_dir.type.keys(): + prev_key = prev_dir.type['type'] + prev_num = prev_dir.type['number'] + else: + continue + if prev_num == dir.type['number']: + if prev_key == "crescendo": + dir.type['type'] = 'crescendo' + break + elif prev_key == "diminuendo": + dir.type['type'] = 'diminuendo' + break + cleaned_direction.append(dir) + else: + print(vars(dir.xml_direction)) + + return cleaned_direction + + def get_beat_positions(self, in_measure_level=False): + piano = self.parts[0] + num_measure = len(piano.measures) + time_signatures = self.get_time_signatures() + time_sig_position = [time.xml_position for time in time_signatures] + beat_piece = [] + for i in range(num_measure): + measure = piano.measures[i] + measure_start = measure.start_xml_position + corresp_time_sig_idx = self.binary_index(time_sig_position, measure_start) + corresp_time_sig = time_signatures[corresp_time_sig_idx] + # corresp_time_sig = measure.time_signature + full_measure_length = corresp_time_sig.state.divisions * corresp_time_sig.numerator / corresp_time_sig.denominator * 4 + if i < num_measure - 1: + actual_measure_length = piano.measures[i + 1].start_xml_position - measure_start + else: + actual_measure_length = full_measure_length + + # if i +1 < num_measure: + # measure_length = piano.measures[i+1].start_xml_position - measure_start + # else: + # measure_length = measure_start - piano.measures[i-1].start_xml_position + + num_beat_in_measure = corresp_time_sig.numerator + if in_measure_level: + num_beat_in_measure = 1 + elif num_beat_in_measure == 6: + num_beat_in_measure = 2 + elif num_beat_in_measure == 9: + num_beat_in_measure = 3 + elif num_beat_in_measure == 12: + num_beat_in_measure = 4 + elif num_beat_in_measure == 18: + num_beat_in_measure = 3 + elif num_beat_in_measure == 24: + num_beat_in_measure = 4 + inter_beat_interval = full_measure_length / num_beat_in_measure + if actual_measure_length != full_measure_length: + measure.implicit = True + else: + measure.implicit = False + + if measure.implicit: + length_ratio = actual_measure_length / full_measure_length + minimum_beat = 1 / num_beat_in_measure + num_beat_in_measure = int(math.ceil(length_ratio / minimum_beat)) + if i == 0: + for j in range(-num_beat_in_measure, 0): + beat = piano.measures[i + 1].start_xml_position + j * inter_beat_interval + if len(beat_piece) > 0 and beat > beat_piece[-1]: + beat_piece.append(beat) + elif len(beat_piece) == 0: + beat_piece.append(beat) + else: + for j in range(0, num_beat_in_measure): + beat = piano.measures[i].start_xml_position + j * inter_beat_interval + if beat > beat_piece[-1]: + beat_piece.append(beat) + else: + for j in range(num_beat_in_measure): + beat = measure_start + j * inter_beat_interval + beat_piece.append(beat) + # + # for note in measure.notes: + # note.on_beat = check_note_on_beat(note, measure_start, measure_length) + return beat_piece + + def get_accidentals(self): + directions = [] + accs = ['#', '♭', '♮'] + # accs = ' # ♭ ♮ ' + for part in self.parts: + for measure in part.measures: + for direction in measure.directions: + if direction.type['type'] == 'words' and direction.type['content'] in accs: + directions.append(direction) + return directions + + def binary_index(self, alist, item): + # better to move : utils.py + first = 0 + last = len(alist)-1 + midpoint = 0 + + if(item< alist[first]): + return 0 + + while first item: + return midpoint + else: first = midpoint +1 + if first == last and alist[last] > item: + return midpoint + elif currentElement > item: + last = midpoint -1 + else: + if midpoint +1 ==len(alist): + return midpoint + while alist[midpoint+1] == item: + midpoint += 1 + if midpoint + 1 == len(alist): + return midpoint + return midpoint + return last diff --git a/musicxml_parser/mxp/measure.py b/musicxml_parser/mxp/measure.py new file mode 100644 index 0000000..a380bc4 --- /dev/null +++ b/musicxml_parser/mxp/measure.py @@ -0,0 +1,283 @@ +from fractions import Fraction + +from . import constants +from .chord_symbol import ChordSymbol +from .tempo import Tempo +from .time_signature import TimeSignature +from .key_signature import KeySignature +from .exception import MultipleTimeSignatureException +from .note import Note +from .direction import Direction + + +class Measure(object): + """Internal represention of the MusicXML element.""" + + def __init__(self, xml_measure, state): + self.xml_measure = xml_measure + self.notes = [] + self.directions = [] + self.chord_symbols = [] + self.tempos = [] + self.time_signature = None + self.key_signature = None + self.barline = None # 'double' or 'final' or None + self.repeat = None # 'start' or 'jump' or None + self.segno = None # 'start' or 'jump' or None + self.coda = None # 'start' or 'jump' or None + self.dacapo = None # 'jump' or None + self.fine = False # True or False + self.first_ending_start = False # 1 or 2 or None + self.first_ending_stop = False # 'start' or 'end' or None + + # Cumulative duration in MusicXML duration. + # Used for time signature calculations + self.duration = 0 + self.implicit = False + self.state = state + # Record the starting time of this measure so that time signatures + # can be inserted at the beginning of the measure + self.start_time_position = self.state.time_position + self.start_xml_position = self.state.xml_position + self._parse() + # Update the time signature if a partial or pickup measure + # self._fix_time_signature() + + def _parse(self): + """Parse the element.""" + # Create new direction + # direction = [] + if 'implicit' in self.xml_measure.attrib.keys(): + self.implicit = self.xml_measure.attrib['implicit'] + for child in self.xml_measure: + + if child.tag == 'attributes': + self._parse_attributes(child) + elif child.tag == 'backup': + self._parse_backup(child) + elif child.tag == 'barline': + self._parse_barline(child) + elif child.tag == 'direction': + # Get tempo in and update state tempo and time_position + self._parse_direction(child) + direction = Direction(child, self.state) + self.directions.append(direction) + # self.state.previous_direction = direction + elif child.tag == 'forward': + self._parse_forward(child) + elif child.tag == 'harmony': + chord_symbol = ChordSymbol(child, self.state) + self.chord_symbols.append(chord_symbol) + elif child.tag == 'note': + note = Note(child, self.state) + self.notes.append(note) + # Keep track of current note as previous note for chord timings + self.state.previous_note_duration = note.note_duration.duration + self.state.previous_note_time_position = note.note_duration.time_position + self.state.previous_note_xml_position = note.note_duration.xml_position + + # Sum up the MusicXML durations in voice 1 of this measure + if note.voice == 1 and not note.is_in_chord: + self.duration += note.note_duration.duration + else: + # Ignore other tag types because they are not relevant. + pass + + def _parse_barline(self, xml_barline): + """Parse the MusicXML element. + + Args: + xml_barline: XML element with tag type 'barline'. + """ + style = xml_barline.find('bar-style') + if style is not None: + style = xml_barline.find('bar-style').text + repeat = xml_barline.find('repeat') + ending = xml_barline.find('ending') + + if style == 'light-light': + self.barline = 'double' + elif style == 'light-heavy': + self.barline = 'final' + + if repeat is not None: + attrib = repeat.attrib['direction'] + if attrib == 'forward': + self.repeat = 'start' + elif attrib == 'backward': + self.repeat = 'jump' + + if ending is not None: + ending_num = ending.attrib['number'] + ending_type = ending.attrib['type'] + if ending_num == '1' and ending_type == 'start': + self.first_ending_start = True + elif ending_num == '1' and ending_type == 'stop': + self.first_ending_stop = True + elif ending_num == '1' and ending_type == 'discontinue': + self.state.first_ending_discontinue = True + + def _parse_attributes(self, xml_attributes): + """Parse the MusicXML element.""" + + for child in xml_attributes: + if child.tag == 'divisions': + self.state.divisions = int(child.text) + elif child.tag == 'key': + self.key_signature = KeySignature(self.state, child) + elif child.tag == 'time': + if self.time_signature is None: + self.time_signature = TimeSignature(self.state, child) + self.state.time_signature = self.time_signature + else: + raise MultipleTimeSignatureException('Multiple time signatures') + elif child.tag == 'transpose': + transpose = int(child.find('chromatic').text) + self.state.transpose = transpose + if self.key_signature is not None: + # Transposition is chromatic. Every half step up is 5 steps backward + # on the circle of fifths, which has 12 positions. + key_transpose = (transpose * -5) % 12 + new_key = self.key_signature.key + key_transpose + # If the new key has >6 sharps, translate to flats. + # TODO(fjord): Could be more smart about when to use sharps vs. flats + # when there are enharmonic equivalents. + if new_key > 6: + new_key %= -6 + self.key_signature.key = new_key + + else: + # Ignore other tag types because they are not relevant to mxp. + pass + + def _parse_backup(self, xml_backup): + """Parse the MusicXML element. + + This moves the global time position backwards. + + Args: + xml_backup: XML element with tag type 'backup'. + """ + + xml_duration = xml_backup.find('duration') + backup_duration = int(xml_duration.text) + midi_ticks = backup_duration * (constants.STANDARD_PPQ + / self.state.divisions) + seconds = ((midi_ticks / constants.STANDARD_PPQ) + * self.state.seconds_per_quarter) + self.state.time_position -= seconds + self.state.xml_position -= backup_duration + + def _parse_direction(self, xml_direction): + """Parse the MusicXML element.""" + for child in xml_direction: + if child.tag == 'sound': + if child.get('tempo') is not None: + tempo = Tempo(self.state, child) + self.tempos.append(tempo) + self.state.qpm = tempo.qpm + self.state.seconds_per_quarter = 60 / self.state.qpm + if child.get('dynamics') is not None: + self.state.velocity = int(child.get('dynamics')) + elif child.get('dacapo') is not None: + self.dacapo = 'jump' + elif child.get('fine') is not None: + self.fine = True + elif child.get('alcoda') is not None: + self.coda = 'jump' + elif child.get('coda') is not None: + self.coda = 'start' + elif child.get('dalsegno') is not None: + self.segno = 'jump' + elif child.get('segno') is not None: + self.segno = 'start' + if self.state.first_ending_discontinue and child.tag=='direction-type': + child_list = child.getchildren() + for sub_child in child_list: + if sub_child.tag=='bracket' and sub_child.get('type')=='stop': + self.first_ending_stop = True + self.state.first_ending_discontinue = False + + + def _parse_forward(self, xml_forward): + """Parse the MusicXML element. + + This moves the global time position forward. + + Args: + xml_forward: XML element with tag type 'forward'. + """ + + xml_duration = xml_forward.find('duration') + forward_duration = int(xml_duration.text) + midi_ticks = forward_duration * (constants.STANDARD_PPQ + / self.state.divisions) + seconds = ((midi_ticks / constants.STANDARD_PPQ) + * self.state.seconds_per_quarter) + self.state.time_position += seconds + self.state.xml_position += forward_duration + + def _fix_time_signature(self): + """Correct the time signature for incomplete measures. + + If the measure is incomplete or a pickup, insert an appropriate + time signature into this Measure. + """ + # Compute the fractional time signature (duration / divisions) + # Multiply divisions by 4 because division is always parts per quarter note + numerator = self.duration + denominator = self.state.divisions * 4 + fractional_time_signature = Fraction(numerator, denominator) + + if self.state.time_signature is None and self.time_signature is None: + # No global time signature yet and no measure time signature defined + # in this measure (no time signature or senza misura). + # Insert the fractional time signature as the time signature + # for this measure + self.time_signature = TimeSignature(self.state) + self.time_signature.numerator = fractional_time_signature.numerator + self.time_signature.denominator = fractional_time_signature.denominator + self.state.time_signature = self.time_signature + else: + fractional_state_time_signature = Fraction( + self.state.time_signature.numerator, + self.state.time_signature.denominator) + + # Check for pickup measure. Reset time signature to smaller numerator + pickup_measure = False + if numerator < self.state.time_signature.numerator: + pickup_measure = True + + # Get the current time signature denominator + global_time_signature_denominator = self.state.time_signature.denominator + + # If the fractional time signature = 1 (e.g. 4/4), + # make the numerator the same as the global denominator + if fractional_time_signature == 1 and not pickup_measure: + new_time_signature = TimeSignature(self.state) + new_time_signature.numerator = global_time_signature_denominator + new_time_signature.denominator = global_time_signature_denominator + else: + # Otherwise, set the time signature to the fractional time signature + # Issue #674 - Use the original numerator and denominator + # instead of the fractional one + new_time_signature = TimeSignature(self.state) + new_time_signature.numerator = numerator + new_time_signature.denominator = denominator + + new_time_sig_fraction = Fraction(numerator, + denominator) + + if new_time_sig_fraction == fractional_time_signature: + new_time_signature.numerator = fractional_time_signature.numerator + new_time_signature.denominator = fractional_time_signature.denominator + + # Insert a new time signature only if it does not equal the global + # time signature. + if (pickup_measure or + (self.time_signature is None + and (fractional_time_signature != fractional_state_time_signature))): + new_time_signature.time_position = self.start_time_position + new_time_signature.xml_position = self.start_xml_position + self.time_signature = new_time_signature + self.state.time_signature = new_time_signature diff --git a/musicxml_parser/mxp/notations.py b/musicxml_parser/mxp/notations.py new file mode 100644 index 0000000..a4a86d6 --- /dev/null +++ b/musicxml_parser/mxp/notations.py @@ -0,0 +1,143 @@ +from fractions import Fraction +import xml.etree.ElementTree as ET +import zipfile +from .exception import UnpitchedNoteException, PitchStepParseException + + +class Notations(object): + """Internal representation of a MusicXML Note's Notations properties. + + This represents musical notations symbols, articulationsNo with ten components: + + 1) accent + 2) arpeggiate + 3) fermata + 4) mordent + 5) staccato + 6) tenuto + 7) tie + 8) tied + 9) trill + 10) tuplet + 11) cue (small note) + + """ + + def __init__(self, xml_notations=None): + self.xml_notations = xml_notations + self.is_accent = False + self.is_arpeggiate = False + self.is_fermata = False + self.is_mordent = False + self.is_staccato = False + self.is_tenuto = False + self.tie = None # 'start' or 'stop' or None + self.tied_start = False + self.tied_stop = False + self.is_trill = False + self.is_tuplet = False + self.is_strong_accent = False + self.is_cue = False + self.is_beam_start = False + self.is_beam_continue = False + self.is_beam_stop = False + self.wavy_line = None + self.slurs = [] + self.is_slur_start = False + self.is_slur_stop = False + self.is_slur_continue = False + self.is_slash = False + + def parse_notations(self, xml_notations): + """Parse the MusicXML element.""" + self.xml_notations = xml_notations + if self.xml_notations is not None: + notations = self.xml_notations.getchildren() + for child in notations: + if child.tag == 'articulations': + self._parse_articulations(child) + elif child.tag == 'arpeggiate': + self.is_arpeggiate = True + elif child.tag == 'fermata': + self.is_fermata = True + elif child.tag == 'tie': + self.tie = child.attrib['type'] + elif child.tag == 'tied': + if child.attrib['type'] == 'start': + self.tied_start = True + elif child.attrib['type'] == 'stop': + self.tied_stop = True + elif child.tag == 'ornaments': + self._parse_ornaments(child) + elif child.tag == 'slur': + self._parse_slur(child) + + def _parse_articulations(self, xml_articulation): + """Parse the MusicXML element. + + Args: + xml_articulation: XML element with tag type 'articulation'. + """ + tag = xml_articulation.getchildren()[0].tag + if tag == 'arpeggiate': + self.is_arpeggiate = True + elif tag == 'accent': + self.is_accent = True + elif tag == 'fermata': + self.is_fermata = True + elif tag == 'staccato': + self.is_staccato = True + elif tag == 'tenuto': + self.is_tenuto = True + elif tag == 'tuplet': + self.is_tuplet = True + elif tag == 'strong-accent': + self.is_strong_accent = True + + def _parse_ornaments(self, xml_ornaments): + """Parse the MusicXML element. + + Args: + xml_ornaments: XML element with tag type 'ornaments'. + """ + children = xml_ornaments.getchildren() + for child in children: + tag = child.tag + if tag == 'trill-mark': + self.is_trill = True + if tag == 'inverted-mordent' or tag == 'mordent': + self.is_mordent = True + if tag == 'wavy-line': + type = child.attrib['type'] + if 'number' in child.attrib: + number = child.attrib['number'] + else: + number = 1 + self.wavy_line = WavyLine(type, number) + + def _parse_slur(self, xml_slurs): + type = xml_slurs.attrib['type'] + if 'number' in xml_slurs.attrib: + number = xml_slurs.attrib['number'] + else: + number=1 + self.slurs.append(Slur(type, number)) + + +class WavyLine: + def __init__(self, type, number): + self.type = type # start or stop + self.number = number + self.xml_position = 0 + self.end_xml_position = 0 + self.pitch = 0 + +class Slur: + def __init__(self, type, number): + self.type = type # start or stop + self.number = number + self.xml_position = 0 + self.end_xml_position = 0 + self.index = 0 + self.voice = 0 + diff --git a/musicxml_parser/mxp/note.py b/musicxml_parser/mxp/note.py new file mode 100644 index 0000000..7200ba5 --- /dev/null +++ b/musicxml_parser/mxp/note.py @@ -0,0 +1,233 @@ +from fractions import Fraction +from .exception import UnpitchedNoteException, PitchStepParseException +from .notations import Notations +from .note_duration import NoteDuration +from .note_dynamic import NoteDynamic +from .note_dynamic import NoteTempo +from .note_dynamic import NotePedal + +import copy + + +class Note(object): + """Internal representation of a MusicXML element.""" + + def __init__(self, xml_note, state): + self.xml_note = xml_note + self.voice = 1 + self.is_rest = False + self.is_in_chord = False + # Note.is_grace_note is use to calculate NoteDuration of grace note. + # Therefore, use NoteDuration.is_grace_note. + self.is_grace_note = False + # track whether the note is overlapped + # (share same xml_position and same pitch but shorter duration with another note) + self.is_overlapped = False + self.pitch = None # Tuple (Pitch Name, MIDI number) + self.note_duration = NoteDuration(state) + self.state_fixed = copy.copy(state) + self.state = state + self.note_notations = Notations() + self.dynamic = NoteDynamic() + self.tempo = NoteTempo() + self.staff = 1 + self.chord_index = 0 + self.pedal = NotePedal() + # self.following_note = None # for grace note + self.on_beat = False + self.is_print_object = True + self.following_rest_duration = 0 + self.followed_by_fermata_rest = False + self.measure_number = state.measure_number + self.accidental = None + + self._parse() + + def _parse(self): + """Parse the MusicXML element.""" + + self.midi_channel = self.state.midi_channel + self.midi_program = self.state.midi_program + self.velocity = self.state.velocity + + if 'print-object' in self.xml_note.attrib.keys() and self.xml_note.attrib['print-object'] == 'no': + self.is_print_object = False + + for child in self.xml_note: + if child.tag == 'chord': + self.is_in_chord = True + self.state.chord_index += 1 + self.chord_index = self.state.chord_index + self.note_notations.is_beam_start = self.state.is_beam_start + self.note_notations.is_beam_continue = self.state.is_beam_continue + self.note_notations.is_beam_stop = self.state.is_beam_stop + elif child.tag == 'duration': # if the note is_grace_note, the note does not have 'duration' child. + self.note_duration.parse_duration(self.is_in_chord, self.is_grace_note, child.text) + # if len(self.state.previous_grace_notes) > 0: + # self.apply_previous_grace_notes() + elif child.tag == 'pitch': + self._parse_pitch(child) + elif child.tag == 'rest': + self.is_rest = True + elif child.tag == 'voice': + self.voice = int(child.text) + elif child.tag == 'dot': + self.note_duration.dots += 1 + elif child.tag == 'type': + self.note_duration.type = child.text + elif child.tag == 'time-modification': + # A time-modification element represents a tuplet_ratio + self._parse_tuplet(child) + elif child.tag == 'notations': + self.note_notations.parse_notations(child) + elif child.tag == 'unpitched': + raise UnpitchedNoteException('Unpitched notes are not supported') + elif child.tag == 'grace': + self.note_duration.parse_duration(self.is_in_chord, True, 0) + self.state.previous_grace_notes.append(self) + if 'slash' in child.attrib.keys() and child.attrib['slash'] == 'yes': + self.note_notations.is_slash = True + elif child.tag == 'staff': + self.staff = int(child.text) + elif child.tag == 'cue': + self.note_notations.is_cue = True + elif child.tag == 'beam': + self._parse_beam(child.text) + elif child.tag == 'accidental': + self.accidental = child.text + else: + # Ignore other tag types because they are not relevant to mxp. + pass + + # reset state.chord_index if it is not chord note + if self.is_in_chord == False: + self.state.chord_index = 0 + + def _parse_pitch(self, xml_pitch): + """Parse the MusicXML element.""" + step = xml_pitch.find('step').text + alter_text = '' + alter = 0.0 + if xml_pitch.find('alter') is not None: + alter_text = xml_pitch.find('alter').text + octave = xml_pitch.find('octave').text + + # Parse alter string to a float (floats represent microtonal alterations) + if alter_text: + alter = float(alter_text) + + # Check if this is a semitone alter (i.e. an integer) or microtonal (float) + alter_semitones = int(alter) # Number of semitones + is_microtonal_alter = (alter != alter_semitones) + + # Visual pitch representation + alter_string = '' + if alter_semitones == -2: + alter_string = 'bb' + elif alter_semitones == -1: + alter_string = 'b' + elif alter_semitones == 1: + alter_string = '#' + elif alter_semitones == 2: + alter_string = 'x' + + if is_microtonal_alter: + alter_string += ' (+microtones) ' + + # N.B. - pitch_string does not account for transposition + pitch_string = step + alter_string + octave + + # Compute MIDI pitch number (C4 = 60, C1 = 24, C0 = 12) + midi_pitch = self.pitch_to_midi_pitch(step, alter, octave) + # Transpose MIDI pitch + midi_pitch += self.state.transpose + self.pitch = (pitch_string, midi_pitch) + + def _parse_tuplet(self, xml_time_modification): + """Parses a tuplet ratio. + +Represented in MusicXML by the element. + +Args: + xml_time_modification: An xml time-modification element. +""" + numerator = int(xml_time_modification.find('actual-notes').text) + denominator = int(xml_time_modification.find('normal-notes').text) + self.note_duration.tuplet_ratio = Fraction(numerator, denominator) + + @staticmethod + def pitch_to_midi_pitch(step, alter, octave): + """Convert MusicXML pitch representation to MIDI pitch number.""" + pitch_class = 0 + if step == 'C': + pitch_class = 0 + elif step == 'D': + pitch_class = 2 + elif step == 'E': + pitch_class = 4 + elif step == 'F': + pitch_class = 5 + elif step == 'G': + pitch_class = 7 + elif step == 'A': + pitch_class = 9 + elif step == 'B': + pitch_class = 11 + else: + # Raise exception for unknown step (ex: 'Q') + raise PitchStepParseException('Unable to parse pitch step ' + step) + pitch_class = (pitch_class + int(alter)) + midi_pitch = (12 + pitch_class) + (int(octave) * 12) + return midi_pitch + + def _parse_beam(self, beam_text): + if beam_text == 'begin': + self.note_notations.is_beam_start = True + self.state.is_beam_start = True + elif beam_text == 'end': + self.note_notations.is_beam_stop = True + self.state.is_beam_stop = True + elif beam_text == 'continue': + self.note_notations.is_beam_continue = True + self.state.is_beam_continue = True + + + def __str__(self): + note_string = '{duration: ' + str(self.note_duration.duration) + note_string += ', midi_ticks: ' + str(self.note_duration.midi_ticks) + note_string += ', seconds: ' + str(self.note_duration.seconds) + if self.is_rest: + note_string += ', rest: ' + str(self.is_rest) + else: + note_string += ', pitch: ' + self.pitch[0] + note_string += ', MIDI pitch: ' + str(self.pitch[1]) + + note_string += ', voice: ' + str(self.voice) + note_string += ', velocity: ' + str(self.velocity) + '} ' + note_string += '(@time: ' + str(self.note_duration.time_position) + ') ' + note_string += '(@xml: ' + str(self.note_duration.xml_position) + ')' + return note_string + + pass + + def apply_previous_grace_notes(self): + num_grace = 0 + corresp_grace = [] + for grc in self.state.previous_grace_notes: + if grc.voice == self.voice: + num_grace += 1 + corresp_grace.append(grc) + + total_seconds_grace = 0 + for grc in corresp_grace: + temp_duration_grace = self.state.divisions / 8 + print(temp_duration_grace) + if temp_duration_grace * num_grace > self.note_duration.duration / 2: + temp_duration_grace = self.note_duration.duration / 2 / num_grace + grc.note_duration.time_position += total_seconds_grace + grc.note_duration.seconds = temp_duration_grace * self.state.seconds_per_quarter + total_seconds_grace += grc.note_duration.seconds + print(total_seconds_grace) + self.note_duration.time_position += -total_seconds_grace + self.state.previous_grace_notes = [] + print(self) diff --git a/musicxml_parser/mxp/note_duration.py b/musicxml_parser/mxp/note_duration.py new file mode 100644 index 0000000..64cebb7 --- /dev/null +++ b/musicxml_parser/mxp/note_duration.py @@ -0,0 +1,130 @@ +from __future__ import division +from fractions import Fraction +from . import constants +from .exception import InvalidNoteDurationTypeException + + +class NoteDuration(object): + """Internal representation of a MusicXML note's duration properties.""" + + TYPE_RATIO_MAP = {'maxima': Fraction(8, 1), 'long': Fraction(4, 1), + 'breve': Fraction(2, 1), 'whole': Fraction(1, 1), + 'half': Fraction(1, 2), 'quarter': Fraction(1, 4), + 'eighth': Fraction(1, 8), '16th': Fraction(1, 16), + '32nd': Fraction(1, 32), '64th': Fraction(1, 64), + '128th': Fraction(1, 128), '256th': Fraction(1, 256), + '512th': Fraction(1, 512), '1024th': Fraction(1, 1024)} + + def __init__(self, state): + self.duration = 0 # MusicXML duration + self.midi_ticks = 0 # Duration in MIDI ticks + self.seconds = 0 # Duration in seconds + self.time_position = 0 # Onset time in seconds + self.xml_position = 0 + self.dots = 0 # Number of augmentation dots + self._type = 'quarter' # MusicXML duration type + self.tuplet_ratio = Fraction(1, 1) # Ratio for tuplets (default to 1) + self.is_grace_note = True # Assume true until not found + self.state = state + self.preceded_by_grace_note = False # The note is preceded by a grace note(s) + self.grace_order = 0 # If there are multiple grace notes, record the order of notes (-1, -2) + self.num_grace = 0 + self.is_first_grace_note = False + + def parse_duration(self, is_in_chord, is_grace_note, duration): + """Parse the duration of a note and compute timings.""" + self.duration = int(duration) + # Due to an error in Sibelius' export, force this note to have the + # duration of the previous note if it is in a chord + if is_in_chord: + self.duration = self.state.previous_note_duration + + self.midi_ticks = self.duration + self.midi_ticks *= (constants.STANDARD_PPQ / self.state.divisions) + + self.seconds = (self.midi_ticks / constants.STANDARD_PPQ) + self.seconds *= self.state.seconds_per_quarter + + self.time_position = float("{0:.8f}".format(self.state.time_position)) + self.xml_position = self.state.xml_position + + # Not sure how to handle durations of grace notes yet as they + # steal time from subsequent notes and they do not have a + # tag in the MusicXML + self.is_grace_note = is_grace_note + + if is_in_chord: + # If this is a chord, set the time position to the time position + # of the previous note (i.e. all the notes in the chord will have + # the same time position) + self.time_position = self.state.previous_note_time_position + self.xml_position = self.state.previous_note_xml_position + # pass + else: + # Only increment time positions once in chord + self.state.time_position += self.seconds + self.state.xml_position += self.duration + + def _convert_type_to_ratio(self): + """Convert the MusicXML note-type-value to a Python Fraction. + + Examples: + - whole = 1/1 + - half = 1/2 + - quarter = 1/4 + - 32nd = 1/32 + + Returns: + A Fraction object representing the note type. + """ + return self.TYPE_RATIO_MAP[self.type] + + def duration_ratio(self): + """Compute the duration ratio of the note as a Python Fraction. + + Examples: + - Whole Note = 1 + - Quarter Note = 1/4 + - Dotted Quarter Note = 3/8 + - Triplet eighth note = 1/12 + + Returns: + The duration ratio as a Python Fraction. + """ + # Get ratio from MusicXML note type + duration_ratio = Fraction(1, 1) + type_ratio = self._convert_type_to_ratio() + + # Compute tuplet ratio + duration_ratio /= self.tuplet_ratio + type_ratio /= self.tuplet_ratio + + # Add augmentation dots + one_half = Fraction(1, 2) + dot_sum = Fraction(0, 1) + for dot in range(self.dots): + dot_sum += (one_half ** (dot + 1)) * type_ratio + + duration_ratio = type_ratio + dot_sum + + # If the note is a grace note, force its ratio to be 0 + # because it does not have a tag + if self.is_grace_note: + duration_ratio = Fraction(0, 1) + return duration_ratio + + def duration_float(self): + """Return the duration ratio as a float.""" + ratio = self.duration_ratio() + return ratio.numerator / ratio.denominator + + @property + def type(self): + return self._type + + @type.setter + def type(self, new_type): + if new_type not in self.TYPE_RATIO_MAP: + raise InvalidNoteDurationTypeException( + 'Note duration type "{}" is not valid'.format(new_type)) + self._type = new_type diff --git a/musicxml_parser/mxp/note_dynamic.py b/musicxml_parser/mxp/note_dynamic.py new file mode 100644 index 0000000..c156a6f --- /dev/null +++ b/musicxml_parser/mxp/note_dynamic.py @@ -0,0 +1,26 @@ +class NoteDynamic: + def __init__(self): + self.absolute = None + self.relative = [] + self.absolute_position = 0 + self.cresciuto = None + + +class NoteTempo: + def __init__(self): + self.absolute = None + self.relative = [] + self.time_numerator = 0 + self.time_denominator = 0 + self.recently_changed_position = 0 + + +class NotePedal: + def __init__(self): + self.at_start = 0 + self.at_end = 0 + self.refresh = False + self.refresh_time = 0 + self.cut = False + self.cut_time = 0 + self.soft = 0 diff --git a/musicxml_parser/mxp/part.py b/musicxml_parser/mxp/part.py new file mode 100644 index 0000000..29f7f3e --- /dev/null +++ b/musicxml_parser/mxp/part.py @@ -0,0 +1,132 @@ +from .measure import Measure +from .score_part import ScorePart +import xml.etree.ElementTree as ET +import copy + + + +class Part(object): + """Internal represention of a MusicXML element.""" + + def __init__(self, xml_part, score_parts, state): + self.id = '' + self.score_part = None + self.measures = [] + self._state = state + self._parse(xml_part, score_parts) + + def _parse(self, xml_part, score_parts): + """Parse the element.""" + if 'id' in xml_part.attrib: + self.id = xml_part.attrib['id'] + if self.id in score_parts: + self.score_part = score_parts[self.id] + else: + # If this part references a score-part id that was not found in the file, + # construct a default score-part. + self.score_part = ScorePart() + + # Reset the time position when parsing each part + self._state.time_position = 0 + self._state.xml_position = 0 + self._state.midi_channel = self.score_part.midi_channel + self._state.midi_program = self.score_part.midi_program + self._state.transpose = 0 + + xml_measures = xml_part.findall('measure') + measure_length = len(xml_measures) + current_measure_number = 0 + segno_measure = None + previous_forward_repeats = [] + resolved_repeats = [] + resolved_first_ending = [] + end_measure_of_first_ending = [] + fine_activated = False + + while current_measure_number < measure_length: + measure = xml_measures[current_measure_number] + + self._repair_empty_measure(measure) + self._state.measure_number = current_measure_number + old_state = copy.copy(self._state) + parsed_measure = Measure(measure, self._state) + + if parsed_measure.first_ending_start: + if current_measure_number in resolved_first_ending: + ending_index = resolved_first_ending.index(current_measure_number) + current_measure_number = end_measure_of_first_ending[ending_index] + 1 + self._state = old_state + continue + else: + resolved_first_ending.append(current_measure_number) + + self.measures.append(parsed_measure) + + if parsed_measure.first_ending_stop: + end_measure_of_first_ending.append(current_measure_number) + + if parsed_measure.repeat == 'start': + previous_forward_repeats.append(current_measure_number) + current_measure_number += 1 + elif parsed_measure.repeat == 'jump' and current_measure_number not in resolved_repeats: + resolved_repeats.append(current_measure_number) + if len(resolved_first_ending) != len(end_measure_of_first_ending): + end_measure_of_first_ending.append(current_measure_number) + if len(previous_forward_repeats) == 0: + current_measure_number = 0 + else: + current_measure_number = previous_forward_repeats[-1] + del previous_forward_repeats[-1] + elif parsed_measure.dacapo == 'jump': + current_measure_number = 0 + fine_activated = True + elif parsed_measure.fine and fine_activated: + break + else: + current_measure_number += 1 + + + # + # for (measure_number, measure) in enumerate(xml_measures): + # # Issue #674: Repair measures that do not contain notes + # # by inserting a whole measure rest + # self._repair_empty_measure(measure) + # self._state.measure_number = measure_number + # parsed_measure = Measure(measure, self._state) + # self.measures.append(parsed_measure) + + def _repair_empty_measure(self, measure): + """Repair a measure if it is empty by inserting a whole measure rest. + + If a only consists of a element that advances + the time cursor, remove the element and replace + with a whole measure rest of the same duration. + + Args: + measure: The measure to repair. + """ + # Issue #674 - If the element is in a measure without + # any elements, treat it as if it were a whole measure + # rest by inserting a rest of that duration + forward_count = len(measure.findall('forward')) + note_count = len(measure.findall('note')) + if note_count == 0 and forward_count == 1: + # Get the duration of the element + xml_forward = measure.find('forward') + xml_duration = xml_forward.find('duration') + forward_duration = int(xml_duration.text) + + # Delete the element + measure.remove(xml_forward) + + # Insert the new note + new_note = '' + new_note += '' + str(forward_duration) + '' + new_note += '1whole1' + new_note += '' + new_note_xml = ET.fromstring(new_note) + measure.append(new_note_xml) + + def __str__(self): + part_str = 'Part: ' + self.score_part.part_name + return part_str diff --git a/musicxml_parser/mxp/playable_notes.py b/musicxml_parser/mxp/playable_notes.py new file mode 100644 index 0000000..437feca --- /dev/null +++ b/musicxml_parser/mxp/playable_notes.py @@ -0,0 +1,386 @@ +def get_playable_notes(xml_part, melody_only=False): + notes = [] + measure_number = 1 + for measure in xml_part.measures: + for note in measure.notes: + note.measure_number = measure_number + notes.append(note) + measure_number += 1 + + notes, rests = classify_notes(notes, melody_only=melody_only) + mark_preceded_by_grace_note_to_chord_notes(notes) + if melody_only: + notes = delete_chord_notes_for_melody(notes) + notes = apply_tied_notes(notes) + notes.sort(key=lambda x: (x.note_duration.xml_position, + x.note_duration.grace_order, -x.pitch[1])) + notes = check_overlapped_notes(notes) + notes = apply_rest_to_note(notes, rests) + notes = omit_trill_notes(notes) + notes = extract_and_apply_slurs(notes) + # notes = self.rearrange_chord_index(notes) + return notes, rests + + +def classify_notes(notes, melody_only=False): + # classify notes into notes, and rests. + # calculate grace note order, mark note with preceeding grace notes + grace_tmp = [] + rests = [] + notes_tmp = [] + for note in notes: + if melody_only: + if note.voice != 1: + continue + if note.note_duration.is_grace_note: + grace_tmp.append(note) + notes_tmp.append(note) + elif not note.is_rest: + if len(grace_tmp) > 0: + rest_grc = [] + added_grc = [] + grace_order = -1 + for grc in reversed(grace_tmp): + if grc.voice == note.voice: + note.note_duration.preceded_by_grace_note = True + grc.note_duration.grace_order = grace_order + grc.following_note = note + if grc.chord_index == 0: + grace_order -= 1 + added_grc.append(grc) + else: + rest_grc.append(grc) + num_added = abs(grace_order) - 1 + for grc in added_grc: + # grc.note_duration.grace_order /= num_added + grc.note_duration.num_grace = num_added + if abs(grc.note_duration.grace_order) == num_added: + grc.note_duration.is_first_grace_note = True + grace_tmp = rest_grc + notes_tmp.append(note) + else: + assert note.is_rest + if note.is_print_object: + rests.append(note) + + return notes_tmp, rests + + +def mark_preceded_by_grace_note_to_chord_notes(notes): + for note in notes: + if note.note_duration.preceded_by_grace_note: + onset = note.note_duration.xml_position + voice = note.voice + chords = [note for note in notes if + note.note_duration.xml_position == onset and + note.voice == voice and + not note.note_duration.is_grace_note] + for chd in chords: + chd.note_duration.preceded_by_grace_note = True + + +def delete_chord_notes_for_melody(melody_notes): + note_onset_positions = list( + set(note.note_duration.xml_position for note in melody_notes)) + note_onset_positions.sort() + unique_melody = [] + for onset in note_onset_positions: + notes = [ + note for note in melody_notes if note.note_duration.xml_position == onset] + if len(notes) == 1: + unique_melody.append(notes[0]) + else: + notes.sort(key=lambda x: x.pitch[1]) + unique_melody.append(notes[-1]) + + return unique_melody + + +def apply_tied_notes(notes): + tie_clean_list = [] + for i in range(len(notes)): + if notes[i].note_notations.tied_stop == False: + tie_clean_list.append(notes[i]) + else: + for j in reversed(range(len(tie_clean_list))): + if tie_clean_list[j].note_notations.tied_start and tie_clean_list[j].pitch[1] == notes[i].pitch[1]: + tie_clean_list[j].note_duration.seconds += notes[i].note_duration.seconds + tie_clean_list[j].note_duration.duration += notes[i].note_duration.duration + tie_clean_list[j].note_duration.midi_ticks += notes[i].note_duration.midi_ticks + if notes[i].note_notations.slurs: + for slur in notes[i].note_notations.slurs: + tie_clean_list[j].note_notations.slurs.append(slur) + break + return tie_clean_list + + +def check_overlapped_notes(notes): + previous_onset = -1 + notes_on_onset = [] + pitches = [] + for note in notes: + if note.note_duration.is_grace_note: + continue # does not count grace note, because it can have same pitch on same xml_position + if note.note_duration.xml_position > previous_onset: + previous_onset = note.note_duration.xml_position + pitches = [] + pitches.append(note.pitch[1]) + notes_on_onset = [] + notes_on_onset.append(note) + else: # note has same xml_position + if note.pitch[1] in pitches: # same pitch with same + index_of_same_pitch_note = pitches.index(note.pitch[1]) + previous_note = notes_on_onset[index_of_same_pitch_note] + if previous_note.note_duration.duration > note.note_duration.duration: + note.is_overlapped = True + else: + previous_note.is_overlapped = True + else: + pitches.append(note.pitch[1]) + notes_on_onset.append(note) + + return notes + + +def apply_rest_to_note(notes, rests): + xml_positions = [note.note_duration.xml_position for note in notes] + # concat continuous rests + new_rests = [] + num_rests = len(rests) + for i in range(num_rests): + rest = rests[i] + j = 1 + current_end = rest.note_duration.xml_position + rest.note_duration.duration + current_voice = rest.voice + while i + j < num_rests - 1: + next_rest = rests[i + j] + if next_rest.note_duration.duration == 0: + break + if next_rest.note_duration.xml_position == current_end and next_rest.voice == current_voice: + rest.note_duration.duration += next_rest.note_duration.duration + next_rest.note_duration.duration = 0 + current_end = rest.note_duration.xml_position + rest.note_duration.duration + if next_rest.note_notations.is_fermata: + rest.note_notations.is_fermata = True + elif next_rest.note_duration.xml_position > current_end: + break + j += 1 + + if not rest.note_duration.duration == 0: + new_rests.append(rest) + + rests = new_rests + + for rest in rests: + rest_position = rest.note_duration.xml_position + closest_note_index = binary_index(xml_positions, rest_position) + rest_is_fermata = rest.note_notations.is_fermata + + search_index = 0 + while closest_note_index - search_index >= 0: + prev_note = notes[closest_note_index - search_index] + if prev_note.voice == rest.voice: + prev_note_end = prev_note.note_duration.xml_position + prev_note.note_duration.duration + prev_note_with_rest = prev_note_end + prev_note.following_rest_duration + if prev_note_end == rest_position: + prev_note.following_rest_duration = rest.note_duration.duration + if rest_is_fermata: + prev_note.followed_by_fermata_rest = True + elif prev_note_end < rest_position: + break + # elif prev_note_with_rest == rest_position and prev_note.voice == rest.voice: + # prev_note.following_rest_duration += rest.note_duration.duration + search_index += 1 + + return notes + + + +def omit_trill_notes(notes): + def _combine_wavy_lines(wavy_lines): + num_wavy = len(wavy_lines) + for i in reversed(range(num_wavy)): + wavy = wavy_lines[i] + if wavy.type == 'stop': + deleted = False + for j in range(1, i + 1): + prev_wavy = wavy_lines[i - j] + if prev_wavy.type == 'start' and prev_wavy.number == wavy.number: + prev_wavy.end_xml_position = wavy.xml_position + wavy_lines.remove(wavy) + deleted = True + break + if not deleted: + wavy_lines.remove(wavy) + num_wavy = len(wavy_lines) + for i in reversed(range(num_wavy)): + wavy = wavy_lines[i] + if wavy.end_xml_position == 0: + wavy_lines.remove(wavy) + return wavy_lines + + def _apply_wavy_lines(notes, wavy_lines): + xml_positions = [x.note_duration.xml_position for x in notes] + num_notes = len(notes) + omit_indices = [] + for wavy in wavy_lines: + index = binary_index(xml_positions, wavy.xml_position) + while abs(notes[index].pitch[1] - wavy.pitch[1]) > 3 and index > 0 \ + and notes[index - 1].note_duration.xml_position == notes[index].note_duration.xml_position: + index -= 1 + note = notes[index] + wavy_duration = wavy.end_xml_position - wavy.xml_position + note.note_duration.duration = wavy_duration + trill_pitch = note.pitch[1] + next_idx = index + 1 + while next_idx < num_notes and notes[next_idx].note_duration.xml_position < wavy.end_xml_position: + if notes[next_idx].pitch[1] == trill_pitch: + omit_indices.append(next_idx) + next_idx += 1 + + omit_indices.sort() + if len(omit_indices) > 0: + for idx in reversed(omit_indices): + del notes[idx] + + return notes + + num_notes = len(notes) + omit_index = [] + trill_sign = [] + wavy_lines = [] + for i in range(num_notes): + note = notes[i] + if not note.is_print_object: + omit_index.append(i) + if note.accidental: + # TODO: handle accidentals in non-print notes + if note.accidental == 'natural': + pass + elif note.accidental == 'sharp': + pass + elif note.accidental == 'flat': + pass + if note.note_notations.is_trill: + trill = {'xml_pos': note.note_duration.xml_position, 'pitch': note.pitch[1]} + trill_sign.append(trill) + if note.note_notations.wavy_line: + wavy_line = note.note_notations.wavy_line + wavy_line.xml_position = note.note_duration.xml_position + wavy_line.pitch = note.pitch + wavy_lines.append(wavy_line) + + # move trill mark to the highest notes of the onset + if note.note_notations.is_trill: + notes_in_trill_onset = [] + current_position = note.note_duration.xml_position + + search_index = i + while search_index + 1 < num_notes and notes[ + search_index + 1].note_duration.xml_position == current_position: + search_index += 1 + notes_in_trill_onset.append(notes[search_index]) + search_index = i + + while search_index - 1 >= 0 and notes[search_index - 1].note_duration.xml_position == current_position: + search_index -= 1 + notes_in_trill_onset.append(notes[search_index]) + + for other_note in notes_in_trill_onset: + highest_note = note + if other_note.voice == note.voice and other_note.pitch[1] > highest_note.pitch[ + 1] and not other_note.note_duration.is_grace_note: + highest_note.note_notations.is_trill = False + other_note.note_notations.is_trill = True + + wavy_lines = _combine_wavy_lines(wavy_lines) + + for index in reversed(omit_index): + note = notes[index] + notes.remove(note) + + if len(trill_sign) > 0: + for trill in trill_sign: + for note in notes: + if note.note_duration.xml_position == trill['xml_pos'] and abs(note.pitch[1] - trill['pitch']) < 4 \ + and not note.note_duration.is_grace_note: + note.note_notations.is_trill = True + break + + notes = _apply_wavy_lines(notes, wavy_lines) + + return notes + + +def extract_and_apply_slurs(notes): + resolved_slurs = [] + unresolved_slurs = [] + slur_index = 0 + for note in notes: + slurs = note.note_notations.slurs + if slurs: + for slur in reversed(slurs): + slur.xml_position = note.note_duration.xml_position + slur.voice = note.voice + if slur.type == 'start': + slur.index = slur_index + unresolved_slurs.append(slur) + slur_index += 1 + note.note_notations.is_slur_start = True + elif slur.type == 'stop': + note.note_notations.is_slur_stop = True + for prev_slur in unresolved_slurs: + if prev_slur.number == slur.number and prev_slur.voice == slur.voice: + prev_slur.end_xml_position = slur.xml_position + resolved_slurs.append(prev_slur) + unresolved_slurs.remove(prev_slur) + note.note_notations.slurs.remove(slur) + note.note_notations.slurs.append(prev_slur) + break + + for note in notes: + slurs = note.note_notations.slurs + note_position = note.note_duration.xml_position + if not slurs: + for prev_slur in resolved_slurs: + if prev_slur.voice == note.voice and prev_slur.xml_position <= note_position <= prev_slur.end_xml_position: + note.note_notations.slurs.append(prev_slur) + if prev_slur.xml_position == note_position: + note.note_notations.is_slur_start = True + elif prev_slur.end_xml_position == note_position: + note.note_notations.is_slur_stop = True + else: + note.note_notations.is_slur_continue = True + + return notes + + +def binary_index(alist, item): + first = 0 + last = len(alist)-1 + midpoint = 0 + + if(item< alist[first]): + return 0 + + while first item: + return midpoint + else: first = midpoint +1 + if first == last and alist[last] > item: + return midpoint + elif currentElement > item: + last = midpoint -1 + else: + if midpoint +1 ==len(alist): + return midpoint + while alist[midpoint+1] == item: + midpoint += 1 + if midpoint + 1 == len(alist): + return midpoint + return midpoint + return last diff --git a/musicxml_parser/mxp/score_part.py b/musicxml_parser/mxp/score_part.py new file mode 100644 index 0000000..e64a394 --- /dev/null +++ b/musicxml_parser/mxp/score_part.py @@ -0,0 +1,50 @@ +from .tempo import Tempo +from .chord_symbol import ChordSymbol +from .note import Note + +DEFAULT_MIDI_PROGRAM = 0 # Default MIDI Program (0 = grand piano) +DEFAULT_MIDI_CHANNEL = 0 # Default MIDI Channel (0 = first channel) + + +class ScorePart(object): + """"Internal representation of a MusicXML . + + A element contains MIDI program and channel info + for the elements in the MusicXML document. + + If no MIDI info is found for the part, use the default MIDI channel (0) + and default to the Grand Piano program (MIDI Program #1). + """ + + def __init__(self, xml_score_part=None): + self.id = '' + self.part_name = '' + self.midi_channel = DEFAULT_MIDI_CHANNEL + self.midi_program = DEFAULT_MIDI_PROGRAM + if xml_score_part is not None: + self._parse(xml_score_part) + + def _parse(self, xml_score_part): + """Parse the element to an in-memory representation.""" + self.id = xml_score_part.attrib['id'] + + if xml_score_part.find('part-name') is not None: + self.part_name = xml_score_part.find('part-name').text or '' + + xml_midi_instrument = xml_score_part.find('midi-instrument') + if (xml_midi_instrument is not None and + xml_midi_instrument.find('midi-channel') is not None and + xml_midi_instrument.find('midi-program') is not None): + self.midi_channel = int(xml_midi_instrument.find('midi-channel').text) + self.midi_program = int(xml_midi_instrument.find('midi-program').text) + else: + # If no MIDI info, use the default MIDI channel. + self.midi_channel = DEFAULT_MIDI_CHANNEL + # Use the default MIDI program + self.midi_program = DEFAULT_MIDI_PROGRAM + + def __str__(self): + score_str = 'ScorePart: ' + self.part_name + score_str += ', Channel: ' + str(self.midi_channel) + score_str += ', Program: ' + str(self.midi_program) + return score_str diff --git a/musicxml_parser/mxp/tempo.py b/musicxml_parser/mxp/tempo.py new file mode 100644 index 0000000..86e4f01 --- /dev/null +++ b/musicxml_parser/mxp/tempo.py @@ -0,0 +1,32 @@ +from . import constants +import copy + + +class Tempo(object): + """Internal representation of a MusicXML tempo.""" + + def __init__(self, state, xml_sound=None): + self.xml_sound = xml_sound + self.qpm = -1 + self.time_position = -1 + self.xml_position = -1 + self.state = copy.copy(state) + if xml_sound is not None: + self._parse() + + def _parse(self): + """Parse the MusicXML element and retrieve the tempo. + + If no tempo is specified, default to DEFAULT_QUARTERS_PER_MINUTE + """ + self.qpm = float(self.xml_sound.get('tempo')) + if self.qpm == 0: + # If tempo is 0, set it to default + self.qpm = constants.DEFAULT_QUARTERS_PER_MINUTE + self.time_position = self.state.time_position + self.xml_position = self.state.xml_position + + def __str__(self): + tempo_str = 'Tempo: ' + str(self.qpm) + tempo_str += ' (@time: ' + str(self.time_position) + ')' + return tempo_str diff --git a/musicxml_parser/mxp/time_signature.py b/musicxml_parser/mxp/time_signature.py new file mode 100644 index 0000000..8232c91 --- /dev/null +++ b/musicxml_parser/mxp/time_signature.py @@ -0,0 +1,66 @@ +from .exception import AlternatingTimeSignatureException, TimeSignatureParseException +import copy + + +class TimeSignature(object): + """Internal representation of a MusicXML time signature. + + Does not support: + - Composite time signatures: 3+2/8 + - Alternating time signatures 2/4 + 3/8 + - Senza misura + """ + + def __init__(self, state, xml_time=None): + self.xml_time = xml_time + self.numerator = -1 + self.denominator = -1 + self.time_position = 0 + self.xml_position = 0 + self.state = copy.copy(state) + if xml_time is not None: + self._parse() + + def _parse(self): + """Parse the MusicXML