Source code for scripts.infer_pitches

#!/usr/bin/env python
"""This is a script that takes the full-grown notation graph
and recovers for each notehead the pitch to which it corresponds.

Assumptions
-----------

* Clefs are used in a standard way: G-clef on 4th, C-clef on 3rd, F-clef
  on 2nd staffline.
* Key signatures are used in a standard way, so that we can rely on counting
  the accidentals.
* Accidentals are valid up until the end of the bar.

We are currently NOT processing any transpositions.

Representation
--------------

Notes are not noteheads. Pitch is associated with a note, and it is derived
from the notehead's subgraph. The current goal of this exercise is obtaining
MIDI, so we discard in effect information about what is e.g. a G-sharp
and A-flat.

"""
from __future__ import print_function, unicode_literals, division
import argparse
import copy
import logging
import os
import pprint
import time

import collections

import itertools
import numpy

from muscima.io import parse_cropobject_list, export_cropobject_list
from muscima.cropobject import link_cropobjects
from muscima.inference_engine_constants import InferenceEngineConstants

__version__ = "0.0.1"
__author__ = "Jan Hajic jr."

from muscima.inference import PitchInferenceEngine, OnsetsInferenceEngine,  MIDIBuilder


##############################################################################


[docs]def build_argument_parser(): parser = argparse.ArgumentParser(description=__doc__, add_help=True, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('-a', '--annot', action='store', required=True, help='The annotation file for which the staffline and staff' ' CropObject relationships should be added.') parser.add_argument('-e', '--export', action='store', help='A filename to which the output CropObjectList' ' should be saved. If not given, will print to' ' stdout.') parser.add_argument('-m', '--midi', action='store', help='A filename to which to export the MIDI file' ' for the given score.') parser.add_argument('-v', '--verbose', action='store_true', help='Turn on INFO messages.') parser.add_argument('--debug', action='store_true', help='Turn on DEBUG messages.') return parser
##############################################################################
[docs]def main(args): logging.info('Starting main...') _start_time = time.clock() # Your code goes here if not os.path.isfile(args.annot): raise ValueError('Annotation file {0} not found!' ''.format(args.annot)) cropobjects = parse_cropobject_list(args.annot) pitch_inference_engine = PitchInferenceEngine() time_inference_engine = OnsetsInferenceEngine(cropobjects=cropobjects) logging.info('Running pitch inference.') pitches, pitch_names = pitch_inference_engine.infer_pitches(cropobjects, with_names=True) # durations = inference_engine.durations_beats # Logging #pitch_names = {objid: midi2pitch_name(midi_code) # for objid, midi_code in pitches.items()} # Export logging.info('Adding pitch information to <Data> attributes.') for c in cropobjects: if c.objid in pitches: midi_pitch_code = pitches[c.objid] pitch_step, pitch_octave = pitch_names[c.objid] # beats = durations[c.objid] # if len(beats) > 1: # logging.warn('Notehead {0}: multiple possible beats: {1}' # ''.format(c.uid, beats)) # b = beats[0] # else: # b = beats[0] if c.data is None: c.data = dict() c.data['midi_pitch_code'] = midi_pitch_code c.data['normalized_pitch_step'] = pitch_step c.data['pitch_octave'] = pitch_octave logging.info('Adding duration info to <Data> attributes.') durations = time_inference_engine.durations(cropobjects) logging.info('Total durations: {0}'.format(len(durations))) for c in cropobjects: if c.objid in durations: c.data['duration_beats'] = durations[c.objid] logging.info('Some durations: {0}'.format(sorted(durations.items())[:10])) logging.info('Adding onset info to <Data> attributes.') onsets = time_inference_engine.onsets(cropobjects) logging.info('Total onsets: {0}'.format(len(onsets))) for c in cropobjects: if c.objid in onsets: c.data['onset_beats'] = onsets[c.objid] if args.export is not None: with open(args.export, 'w') as hdl: hdl.write(export_cropobject_list(cropobjects)) hdl.write('\n') else: print(export_cropobject_list(cropobjects)) if args.midi is not None: midi_builder = MIDIBuilder() mf = midi_builder.build_midi(pitches, durations, onsets) with open(args.midi, 'wb') as hdl: mf.writeFile(hdl) _end_time = time.clock() logging.info('infer_pitches.py done in {0:.3f} s'.format(_end_time - _start_time))
############################################################################## if __name__ == '__main__': parser = build_argument_parser() args = parser.parse_args() if args.verbose: logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) if args.debug: logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG) main(args)