Beispiel #1
0
    def load_pairwise_alignment(self, performance_ref, performance_des):
        if performance_ref not in self.performances:
            raise MSMDDBError('Piece {0} in collection {1} does'
                              ' not have a performance with name {2}.'
                              ' Available performances: {3}'
                              ''.format(self.name, self.collection_root,
                                        performance_ref,
                                        self.available_performances))

        if performance_des not in self.performances:
            raise MSMDDBError('Piece {0} in collection {1} does'
                              ' not have a performance with name {2}.'
                              ' Available performances: {3}'
                              ''.format(self.name, self.collection_root,
                                        performance_des,
                                        self.available_performances))

        score = self.load_score(self.available_scores[0])
        mungos = score.load_mungos()
        aln = list()

        for cur_mung in mungos:
            onset_key_ref = performance_ref + '_onset_seconds'
            onset_key_des = performance_des + '_onset_seconds'

            # check if this note has an alignment associated to it
            if onset_key_ref not in cur_mung.data.keys():
                continue

            if onset_key_des not in cur_mung.data.keys():
                continue

            cur_note_onset_ref = cur_mung.data[performance_ref +
                                               '_onset_seconds']
            cur_note_onset_des = cur_mung.data[performance_des +
                                               '_onset_seconds']

            aln.append((cur_note_onset_ref, cur_note_onset_des))

        # sort by appearance in MIDI matrix
        aln.sort(key=itemgetter(1))

        return aln
Beispiel #2
0
    def add_performance(self, name, audio_file=None, midi_file=None,
                        overwrite=False):
        """Creates a new performance in the piece from existing audio
        and optionally MIDI files.

        :param name: Name of the new performance. The performance folder
            will have this name, and the performance audio (and midi) file
            names will be derived from this name by simply copying the format
            suffix from the ``audio_file`` and ``midi_file`` arguments.

        :param audio_file: The audio file for the performance. Will be copied
            into the newly created performance directory, with the filename
            derived as the `name`` plus the format suffix.

        :param midi_file: The performance MIDI. Optional. Will be copied
            into the newly created performance directory. Same name convention
            as for ``audio_file``.

        :param overwrite: If true, if a performance with the given ``name``
            exists, will delete it.
        """
        if (audio_file is None) and (midi_file is None):
            raise ValueError('At least one of audio and midi files'
                             ' has to be supplied to create a performance.')
        if name in self.performances:
            if overwrite:
                logging.info('Piece {0}: performance {1} already exists,'
                             ' overwriting!'.format(self.name, name))
                time.sleep(5)
                self.remove_performance(name)
            else:
                raise MSMDDBError('Piece {0}: performance {1} already'
                                          ' exists!'.format(self.name, name))
        new_performance_dir = os.path.join(self.performance_dir, name)

        # This part should be refactored as performance.build_performance()
        os.mkdir(new_performance_dir)

        audio_fmt = os.path.splitext(audio_file)[-1]
        performance_audio_filename = os.path.join(new_performance_dir,
                                           name + audio_fmt)
        shutil.copyfile(audio_file, performance_audio_filename)

        if midi_file:
            midi_fmt = os.path.splitext(midi_file)[-1]
            performance_midi_filename = os.path.join(new_performance_dir,
                                                     name + midi_fmt)
            shutil.copyfile(midi_file, performance_midi_filename)

        self.update()

        # Test-load the Performance. Ensures folder structure initialization.
        _ = self.load_performance(name,
                                  require_audio=(audio_file is not None),
                                  require_midi=(midi_file is not None))
Beispiel #3
0
 def clear_view(self, view_name):
     """Removes the given view."""
     self.update()
     if view_name not in self.views:
         raise MSMDDBError('Score {0}: requested clearing view'
                           ' {1}, but this view does not exist!'
                           ' (Available views: {2})'
                           ''.format(self.name, view_name,
                                     self.views.keys()))
     shutil.rmtree(self.views[view_name])
     self.update()
Beispiel #4
0
 def load_score(self, score_name):
     self.update()
     if score_name not in self.scores:
         raise MSMDDBError('Piece {0} in collection {1} does'
                           ' not have a score with name {2}.'
                           ' Available scores: {3}'
                           ''.format(self.name, self.collection_root,
                                     score_name, self.available_scores))
     score_dir = self.scores[score_name]
     score = Score(folder=score_dir, piece_name=self.name)
     return score
Beispiel #5
0
    def _load_feature_by_suffix(self, suffix):
        """Utility function for loading features by suffix naming
        conventions."""
        self.update_features()
        candidate_feature_names = [
            f for f in self.features if f.endswith(suffix)
        ]
        if len(candidate_feature_names) == 0:
            raise MSMDDBError('Performance {0}: Feature {1}'
                              ' not available! Availble feature'
                              ' names: {2}'.format(self.name, suffix,
                                                   self.features.keys()))
        if len(candidate_feature_names) > 1:
            raise MSMDDBError('Performance {0}: More than one feature'
                              ' conforms to the suffix {1}: {2}'
                              ''.format(self.name, suffix,
                                        candidate_feature_names))

        feature_name = candidate_feature_names[0]
        return self.load_feature(feature_name)
Beispiel #6
0
 def view_files(self, view_name):
     """Return a list of the paths to all (non-hidden) files
     in the view."""
     self.update()
     if view_name not in self.views:
         raise MSMDDBError('Score {0}: requested view {1}'
                           ' not available!'
                           ''.format(self.name, view_name))
     view_dir = self.views[view_name]
     return [
         os.path.join(view_dir, f) for f in sorted(os.listdir(view_dir))
         if (not f.startswith('.')) and (
             os.path.isfile(os.path.join(view_dir, f)))
     ]
Beispiel #7
0
 def load_performance(self, performance_name, **perf_kwargs):
     """Creates a ``Performance`` object for the given performance
     and returns it. You can pass Performance initialization kwargs."""
     self.update()
     if performance_name not in self.performances:
         raise MSMDDBError('Piece {0} in collection {1} does'
                           ' not have a performance with name {2}.'
                           ' Available performances: {3}'
                           ''.format(self.name, self.collection_root,
                                     performance_name,
                                     self.available_performances))
     performance_dir = self.performances[performance_name]
     performance = Performance(folder=performance_dir,
                               piece_name=self.name,
                               **perf_kwargs)
     return performance
Beispiel #8
0
 def _set_authority(self, authority_format):
     """Sets the authority to the selected format. Don't do this
     unless you are sure what you are doing. If you really need
     to derive something in the piece from different authority
     encodings, consider initializing another ``Piece`` instance."""
     if authority_format not in self.AVAILABLE_AUTHORITIES:
         raise ValueError('Authority format not supported: {0}'
                          ''.format(authority_format))
     if authority_format not in self.encodings:
         raise MSMDDBError('Piece {0} in collection {1} does'
                           ' not have the requested authority'
                           ' encoding {2}. (Available encodings:'
                           ' {3}'.format(self.name, self.collection_root,
                                         authority_format,
                                         self.encodings.values()))
     self.authority_format = authority_format
     self.authority = self.encodings[authority_format]
Beispiel #9
0
    def load_feature(self, feature_name):
        """Loads the feature with the given name, if available
        in self.features. Raises a ValueError otherwise."""
        self.collect_features()
        if feature_name not in self.features:
            raise ValueError('Performance {0}: feature {1} not available!'
                             ' Available feature names: {2}'
                             ''.format(self.name, feature_name,
                                       self.features.keys()))

        if not os.path.isfile(self.features[feature_name]):
            raise MSMDDBError('Performance {0}: feature {1} is'
                              ' available, but the file {2} does not'
                              ' exist...?'
                              ''.format(self.name, feature_name,
                                        self.features[feature_name]))

        feature = numpy.load(self.features[feature_name])
        return feature
Beispiel #10
0
    def load_mungos(self, classes=None, by_page=False):
        """Loads all the available MuNG objects as a list. You need to make
        sure the objids don't clash across pages!"""
        self.update()
        if 'mung' not in self.views:
            raise MSMDDBError('Score {0}: mung view not available!'
                              ''.format(self.name))
        mung_files = self.view_files('mung')

        mungos = []
        for f in mung_files:
            ms = parse_cropobject_list(f)
            if by_page:
                mungos.append(ms)
            else:
                mungos.extend(ms)

        if classes is not None:
            mungos = [m for m in mungos if m.clsname in classes]

        return mungos
Beispiel #11
0
    def __init__(self,
                 folder,
                 piece_name,
                 audio_fmt='flac',
                 require_audio=True,
                 require_midi=True):
        """Initialize Performance.

        :param audio_fmt: The audio of the performance is expected
            to have this format.
        """
        super(Performance, self).__init__()

        if not os.path.isdir(folder):
            raise MSMDDBError('Performance initialized with'
                              ' non-existent directory: {0}'
                              ''.format(folder))
        self.folder = folder
        name = path2name(folder)
        self.name = name
        self.piece_name = piece_name

        self.metadata = self.load_metadata()

        if audio_fmt.startswith('.'):
            audio_fmt = audio_fmt[1:]
        self.audio_fmt = audio_fmt

        self.audio = self.discover_audio(required=require_audio)
        self.audio_name = None
        if self.audio:
            self.audio_name = path2name(self.audio)

        self.midi = self.discover_midi(required=require_midi)

        self.features_dir = os.path.join(self.folder, 'features')
        self._ensure_features_dir()

        self.features = self.collect_features()
Beispiel #12
0
    def get_ordered_notes(self,
                          filter_tied=False,
                          reverse_columns=False,
                          return_columns=False):
        """Returns the MuNG objects corresponding to notes in the canonical
        ordering: by page, system, left-to-right, and top-down within
        simultaneities (e.g. chords).

        :param reverse_columns: If set, will order the columns bottom-up
            instead of top-down. Use this for events alignment, not for score
            inference.
        """
        self.update()
        if 'mung' not in self.views:
            raise MSMDDBError('Score {0}: mung view not available!'
                              ''.format(self.name))
        mung_files = self.view_files('mung')

        # Algorithm:
        #  - Create hard ordering constraints:
        #     - pages (already done: mungos_per_page)
        #     - systems

        notes_per_page = []

        for f in mung_files:
            mungos = parse_cropobject_list(f)
            mgraph = NotationGraph(mungos)
            _CONST = InferenceEngineConstants()

            note_mungos = [c for c in mungos if 'midi_pitch_code' in c.data]
            system_mungos = [c for c in mungos if c.clsname == 'staff']
            system_mungos = sorted(system_mungos, key=lambda m: m.top)

            notes_per_system = []

            for s in system_mungos:
                system_notes = mgraph.ancestors(
                    s, classes=_CONST.NOTEHEAD_CLSNAMES)
                for c in system_notes:
                    if 'midi_pitch_code' not in c.data:
                        print('Notehead without pitch: {0}' ''.format(str(c)))
                        continue
                    if c.data['midi_pitch_code'] is None:
                        print('Notehead with pitch=None: {0}'
                              ''.format(str(c)))

                system_notes = [
                    c for c in system_notes if 'midi_pitch_code' in c.data
                ]

                # print('Ancestors of system {0}: {1}'.format(s, system_notes))
                # Process simultaneities. We use a very small overlap ratio,
                # because we want to catch also chords that have noteheads
                # on both sides of the stem. Sorted top-down.
                # Remove all tied notes.
                if filter_tied:
                    system_notes = [
                        m for m in system_notes if ('tied' not in m.data) or (
                            ('tied' in m.data) and (m.data['tied'] != 1))
                    ]

                system_note_columns = group_mungos_by_column(
                    system_notes,
                    MIN_OVERLAP_RATIO=0.05,
                    reverse_columns=reverse_columns)
                # print('System {0}: n_columns = {1}'
                #       ''.format(s.objid, len(system_note_columns)))
                ltr_sorted_columns = sorted(system_note_columns.items(),
                                            key=lambda kv: kv[0])
                # print('ltr_sorted_columns[0] = {0}'.format(ltr_sorted_columns[0]))
                system_ordered_simultaneities = [
                    c[1] for c in ltr_sorted_columns
                ]
                # print('system_ordered_sims[0] = {0}'.format(system_ordered_simultaneities[0]))

                notes_per_system.append(system_ordered_simultaneities)

            # print('Total entries in notes_per_system = {0}'.format(len(notes_per_system)))
            notes_per_page.append(notes_per_system)

        # Data structure
        # --------------
        # notes_per_page = [
        #   notes_per_system_1 = [
        #       ordered_simultaneities = [
        #           simultaneity1 = [ a'', f'', c'', a' ],
        #           simultaneity2 = [ g'', e'', c'', bes' ],
        #           ...
        #       ]
        #   ],
        #   notes_per_system_2 = [
        #       simultaneity1 = [ ... ]
        #       ...
        #   ]
        # ]

        # Unroll simultaneities notes according to this data structure

        # DEBUG
        # print('notes_per_page: {0}'.format(pprint.pformat(notes_per_page)))

        ordered_simultaneities = []
        for page in notes_per_page:
            for system in page:
                ordered_simultaneities.extend(system)

        if return_columns:
            return ordered_simultaneities

        ordered_notes = []
        for sim in ordered_simultaneities:
            ordered_notes.extend(list(reversed(sim)))

        return ordered_notes