diff --git a/test/cases/01-klavier/source.png b/test/cases/01-klavier/source.png new file mode 100644 index 000000000..0ab7b6a3f Binary files /dev/null and b/test/cases/01-klavier/source.png differ diff --git a/test/cases/01-klavier/target.xml b/test/cases/01-klavier/target.xml new file mode 100644 index 000000000..19476be3e --- /dev/null +++ b/test/cases/01-klavier/target.xml @@ -0,0 +1,550 @@ + + + + + + MuseScore 2.1.0 + 2018-03-08 + + + + + + + test/01-klavier/source.png + + + + 6.096 + 40 + + + 1948.82 + 1377.95 + + 79.9869 + 79.9869 + 79.9869 + 79.9869 + + + + + + + + Klavier + + Acoustic Grand Piano + + + + 1 + 1 + 77.9528 + 0 + + + + + + + + + 21.00 + 0.00 + + 170.00 + + + 65.00 + + + + 1 + + -1 + + + 2 + + G + 2 + + + F + 4 + + + + + 1 + 1 + quarter + 1 + + + + F + 4 + + 2 + 1 + half + up + 1 + + + + + + + + + + A + -1 + 4 + + 2 + 1 + half + flat + up + 1 + + + + + B + -1 + 4 + + 2 + 1 + half + up + 1 + + + + + D + 5 + + 2 + 1 + half + up + 1 + + + 3 + + + + 1 + 5 + quarter + 2 + + + + D + 4 + + 2 + 5 + half + up + 2 + + + + + + + + 3 + + + + D + 3 + + 3 + 6 + half + + down + 2 + + + + + + + + + + + 1 + 1 + quarter + 1 + + + + B + 3 + + 2 + 1 + half + natural + up + 1 + + + + + + + + + + D + 4 + + 2 + 1 + half + up + 1 + + + + + F + 4 + + 2 + 1 + half + up + 1 + + + + + A + 4 + + 2 + 1 + half + natural + up + 1 + + + 3 + + + + 1 + 5 + quarter + 2 + + + + + + + + 2 + + + + + A + 3 + + 2 + 5 + half + up + 2 + + + 3 + + + + D + 3 + + 3 + 6 + half + + down + 2 + + + + + + + + + + + D + 5 + + 1 + 1 + quarter + 1 + + + + F + 4 + + 2 + 1 + half + up + 1 + + + + + + + + + + A + -1 + 4 + + 2 + 1 + half + flat + up + 1 + + + + + D + 5 + + 2 + 1 + half + up + 1 + + + 3 + + + + D + 4 + + 3 + 2 + half + + down + 1 + + + + + + + + 3 + + + + +

+ + + 2 + + + + + B + -1 + 2 + + 3 + 5 + half + + flat + down + 2 + + + + + + + + + + F + 3 + + 3 + 5 + half + + down + 2 + + + + + + C + 1 + 4 + + 2 + 1 + half + sharp + up + 1 + + + + + + + + + + + A + 4 + + 2 + 1 + half + natural + up + 1 + + + + A + 4 + + 1 + 1 + quarter + up + 1 + + + 3 + + + + F + 4 + + 1 + 2 + quarter + down + 1 + + + + + + + E + 4 + + 1 + 2 + quarter + down + 1 + + + + + + + + 1 + 2 + quarter + 1 + + + 3 + + + + A + 2 + + 2 + 5 + half + natural + down + 2 + + + + + + + + + + + G + 3 + + 2 + 5 + half + down + 2 + + + + 1 + 5 + quarter + 2 + + + light-light + + + + diff --git a/test/diffscore.py b/test/diffscore.py new file mode 100755 index 000000000..9b75f4d68 --- /dev/null +++ b/test/diffscore.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +import os +import tempfile +import logging +import subprocess +import argparse +from collections import defaultdict, namedtuple +import music21 + + +Note = namedtuple('Note', 'pitch offset duration') + + +def pianoroll(score): + result = defaultdict(set) + for measure in score.recurse().getElementsByClass('Measure'): + measureBag = result[measure.number] + for rest in measure.recurse().getElementsByClass('Rest'): + measureBag.add(Note('rest', rest.offset, rest.quarterLength)) + for note in measure.recurse().getElementsByClass('Note'): + measureBag.add(Note(note.pitch, note.offset, note.quarterLength)) + for chord in measure.recurse().getElementsByClass('Chord'): + for pitch in chord.pitches: + measureBag.add(Note(pitch, chord.offset, chord.quarterLength)) + return result + + +def diffMeasures(measure1, measure2): + common = measure1 & measure2 + left = measure1 - measure2 + right = measure2 - measure1 + logging.debug('Common: {}, Left: {}, Right: {}'.format(common, left, right)) + score = len(common) - len(left) - len(right) + logging.debug('Score: %s', score) + return score + + +def diffRolls(roll1, roll2): + score = 0 + barNumbers = sorted(set(roll1.keys()) | set(roll2.keys())) + for n in barNumbers: + logging.debug('Measure %d', n) + score += diffMeasures(roll1.get(n, set()), roll2.get(n, set())) + return score + + +def diffFiles(files): + rolls = [pianoroll(music21.converter.parseFile(f)) for f in files] + score = diffRolls(rolls[0], rolls[1]) + logging.info('Score for {}: {}'.format(files, score)) + return score + + +def find_file(dir, pattern): + head, _, tail = pattern.partition('*') + for d, _, files in os.walk(dir): + for f in files: + if f.startswith(head) and f.endswith(tail): + return os.path.abspath(os.path.join(d, f)) + raise Exception('file matching "{}*" not found in {}'.format(pattern, dir)) + + +def processCases(args): + score = 0 + for case in os.listdir(args.cases): + logging.info('Processing case ' + case) + casedir = os.path.join(args.cases, case) + source = find_file(casedir, 'source*') + target = find_file(casedir, 'target*') + with tempfile.TemporaryDirectory() as tmpdir: + script = args.script.replace('$@', tmpdir).replace('$<', source) + logging.debug('Calling script: ' + script) + subprocess.check_call(script.split(), cwd='..') + result = find_file(tmpdir, '*.mxl') + score += diffFiles((result, target)) + return score + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--cases', help='directory with test cases') + parser.add_argument('-v', '--verbose', action='store_true') + parser.add_argument('--script', help='command to recognize a given score', + default='gradle run -PcmdLineArgs=-batch,-export,-output,$@,--,$<') + args = parser.parse_args() + + if args.verbose: + logging.getLogger().setLevel(logging.DEBUG) + + score = processCases(args) + print('Total score:', score) + + +if __name__ == '__main__': + main()