Giter Club home page Giter Club logo

parangonar's Introduction

Parangonar

Parangonar is a Python package for note alignment of symbolic music. Parangonar uses Partitura as file I/O utility. Note alignments produced py Parangonar can be visualized using the web tool Parangonda

Installation

The easiest way to install the package is via pip from the PyPI (Python Package Index):

pip install parangonar

This will install the latest release of the package and will install all dependencies automatically.

Getting Started

The following code snippets load the contents of a a previously aligned performance and score alignment file (encoded in the match file format).

A new alignment is computed using different note matchers and the predicted alignment are compared to the ground truth:

For an interactive version of these snippets, check the getting_started.ipynb notebook.

1 - Automatic Note Matching: AutomaticNoteMatcher and DualDTWNoteMatcher

import parangonar as pa
import partitura as pt

perf_match, groundtruth_alignment, score_match = pt.load_match(
    filename= pa.EXAMPLE,
    create_score=True
)

# compute note arrays from the loaded score and performance
pna_match = perf_match[0].note_array()
sna_match = score_match[0].note_array()

# match the notes in the note arrays --------------------- DualDTWNoteMatcher
sdm = pa.AutomaticNoteMatcher()
pred_alignment = sdm(sna_match, 
                     pna_match,
                     verbose_time=True)

# compute f-score and print the results
print('------------------')
types = ['match','insertion', 'deletion']
for alignment_type in types:
    precision, recall, f_score = pa.fscore_alignments(pred_alignment, 
                                                      groundtruth_alignment, 
                                                      alignment_type)
    print('Evaluate ',alignment_type)
    print('Precision: ',format(precision, '.3f'),
          'Recall ',format(recall, '.3f'),
          'F-Score ',format(f_score, '.3f'))
    print('------------------')




# this matcher requires grace note info
sna_match = score_match[0].note_array(include_grace_notes=True)

# match the notes in the note arrays --------------------- DualDTWNoteMatcher
sdm = pa.DualDTWNoteMatcher()
pred_alignment = sdm(sna_match, 
                     pna_match,
                     process_ornaments=False,
                     score_part=score_match[0]) # if a score part is passed, ornaments can be handled seperately

# compute f-score and print the results
print('------------------')
types = ['match','insertion', 'deletion']
for alignment_type in types:
    precision, recall, f_score = pa.fscore_alignments(pred_alignment, 
                                                      groundtruth_alignment, 
                                                      alignment_type)
    print('Evaluate ',alignment_type)
    print('Precision: ',format(precision, '.3f'),
          'Recall ',format(recall, '.3f'),
          'F-Score ',format(f_score, '.3f'))
    print('------------------')

Aligning MusicXML Scores and MIDI Performances

import parangonar as pa
import partitura as pt

score = pt.load_score(filename= 'path/to/score_file')
performance = pt.load_performance_midi(filename= 'path/to/midi_file')

# compute note arrays from the loaded score and performance
pna = performance.note_array()
sna = score.note_array()

# match the notes in the note arrays
sdm = pa.AutomaticNoteMatcher()
pred_alignment = sdm(sna, pna)

2 - Anchor Point Alignment: AnchorPointNoteMatcher

import parangonar as pa
import partitura as pt

perf_match, groundtruth_alignment, score_match = pt.load_match(
    filename= pa.EXAMPLE,
    create_score=True
)

# compute note arrays from the loaded score and performance
pna_match = perf_match.note_array()
sna_match = score_match.note_array()

# compute synthetic anchor points every 4 beats
nodes = pa.match.node_array(score_match[0], 
                   perf_match[0], 
                   groundtruth_alignment,
                   node_interval=4)

# match the notes in the note arrays
apdm = pa.AnchorPointNoteMatcher()
pred_alignment = apdm(sna_match, 
                     pna_match,
                     nodes)

# compute f-score and print the results
print('------------------')
types = ['match','insertion', 'deletion']
for alignment_type in types:
    precision, recall, f_score = pa.fscore_alignments(pred_alignment, 
                                                      groundtruth_alignment, 
                                                      alignment_type)
    print('Evaluate ',alignment_type)
    print('Precision: ',format(precision, '.3f'),
          'Recall ',format(recall, '.3f'),
          'F-Score ',format(f_score, '.3f'))
    print('------------------')

3 - Online / Realtime Alignment: OnlineTransformerMatcher and OnlinePureTransformerMatcher

import parangonar as pa
import partitura as pt

perf_match, groundtruth_alignment, score_match = pt.load_match(
    filename= pa.EXAMPLE,
    create_score=True
)

# compute note arrays from the loaded score and performance
pna_match = perf_match[0].note_array()
# this matcher requires grace note info
sna_match = score_match[0].note_array(include_grace_notes=True)

# set up the matcher using the score information: OnlineTransformerMatcher / OnlinePureTransformerMatcher
matcher = pa.OnlinePureTransformerMatcher(sna_match)

# the "offline" method loops over all notes in the performance and calls the "online" method for each one.
pred_alignment = matcher.offline(pna_match)

# compute f-score and print the results
print('------------------')
types = ['match','insertion', 'deletion']
for alignment_type in types:
    precision, recall, f_score = pa.fscore_alignments(pred_alignment, 
                                                      groundtruth_alignment, 
                                                      alignment_type)
    print('Evaluate ',alignment_type)
    print('Precision: ',format(precision, '.3f'),
          'Recall ',format(recall, '.3f'),
          'F-Score ',format(f_score, '.3f'))
    print('------------------')

4 - Visualize Alignment

import parangonar as pa
import partitura as pt

perf_match, alignment, score_match = pt.load_match(
    filename= pa.EXAMPLE,
    create_score=True
)
pna_match = perf_match.note_array()
sna_match = score_match.note_array()

# show or save plot of note alignment
pa.plot_alignment(pna_match,
                sna_match,
                alignment,s
                save_file = False)

# or plot the performance and score as piano rolls given a reference: 
# we can encode errors if given ground truth
# Blue lines indicate correct matches, red lines incorrect ones.
pa.plot_alignment_comparison(pna_match, sna_match, 
                         pred_alignment, groundtruth_alignment)

5 - File I/O for note alignments

Most I/O functions are handled by partitura. For Parangonada:

  • pt.io.importparangonada.load_parangonada_alignment
  • pt.io.importparangonada.load_parangonada_csv
  • pt.io.exportparangonada.save_parangonada_alignment
  • pt.io.exportparangonada.save_parangonada_csv

For (n)ASAP alignments

  • pt.io.importparangonada.load_alignment_from_ASAP
  • pt.io.exportparangonada.save_alignment_for_ASAP

For match files

  • pt.io.importmatch.load_match
  • pt.io.exportmatch.save_match

and a basic interface for saving parangonada-ready csv files is also available:


import partitura as pt
import parangonar as pa

# export a note alignment for visualization with parangonada:
# https://sildater.github.io/parangonada/
pa.match.save_parangonada_csv(alignment, 
                            performance_data,
                            score_data,
                            outdir="path/to/dir")


# import a corrected note alignment from parangonada:
# https://sildater.github.io/parangonada/
alignment = pt.io.importparangonada.load_parangonada_alignment(filename= 'path/to/note_alignment.csv')

# load note alignments of the asap dataset: 
# https://github.com/CPJKU/asap-dataset/tree/note_alignments
alignment = pt.io.importparangonada.load_alignment_from_ASAP(filename= 'path/to/note_alignment.tsv')

6 - Aligned Data

These note-aligned datasets are publically available:

Publications

Two publications are associated with models available in Parangonar. The anchor point-enhanced AnchorPointNoteMatcher and the automatic AutomaticNoteMatcher are this described in:

@article{nasap-dataset,
 title = {Automatic Note-Level Score-to-Performance Alignments in the ASAP Dataset},
 author = {Peter, Silvan David and Cancino-Chacón, Carlos Eduardo and Foscarin, Francesco and McLeod, Andrew Philip and Henkel, Florian and Karystinaios, Emmanouil and Widmer, Gerhard},
 doi = {10.5334/tismir.149},
 journal = {Transactions of the International Society for Music Information Retrieval {(TISMIR)}},
 year = {2023}
}

and the former is used in the creation of the note-aligned (n)ASAP Dataset.

The improved automatic DualDTWNoteMatcher and the online / realtime OnlineTransformerMatcher / OnlinePureTransformerMatcher are described in:

@inproceedings{peter-2023,
  title={Online Symbolic Music Alignment with Offline Reinforcement Learning},
  author={Peter, Silvan David},
  booktitle={International Society for Music Information Retrieval Conference {(ISMIR)}},
  year={2023}
}

Acknowledgments

This work is supported by the European Research Council (ERC) under the EU’s Horizon 2020 research & innovation programme, grant agreement No. 10101937 (”Wither Music?”).

License

The code in this package is licensed under the Apache 2.0 License. For details, please see the LICENSE file.

parangonar's People

Contributors

sildater avatar

Stargazers

Gerald Golka avatar fooevr avatar  avatar Sungkyun Chang avatar  avatar liuliuliuliu avatar Zhipeng Mao avatar  avatar Jingjing Tang avatar Dasaem Jeong avatar Felipe Honorato avatar Julian Reck avatar Alex avatar Ilya Borovik avatar jiyun.park avatar Jared Turner avatar Federico Simonetta avatar  avatar  avatar Yuan-Man avatar Huan Zhang avatar Carlos Eduardo Cancino-Chacón avatar

Watchers

James Cloos avatar  avatar

parangonar's Issues

Some Note ID duplicates show up as both `match`and `deletion` in case of spurious score IDs

import parangonar as pa
import partitura as pt
import pandas as pd

midi_address = PATH_S
score_address = PATH_M

score = pt.load_score(filename= PATH_S)
performance = pt.load_performance_midi(filename= PATH_M)

#unfolding to a version that best matches the number of notes in performance
part_score = score[0]
score_variants = pt.score.make_score_variants(part_score)
score_part_variants_lengths = [score_variant.create_variant_part().note_array().shape[0] for score_variant in score_variants]
best_match = min(score_part_variants_lengths,
                        key=lambda x: abs(x - len(performance.note_array())))
best_match_idx = score_part_variants_lengths.index(best_match)
best_match_score_part = score_variants[best_match_idx].create_variant_part()


pna = performance.note_array()


#alignment without unfolding without updating ids
sna1 = part_score.note_array()
sdm1 = pa.AutomaticNoteMatcher()
pred_alignment1 = sdm1(sna1, pna)
pd.DataFrame(pred_alignment1).to_csv('a1.csv')

#alignment with unfolding without updating ids
sna2 = best_match_score_part.note_array()
sdm2 = pa.AutomaticNoteMatcher()
pred_alignment2 = sdm2(sna2, pna)
pd.DataFrame(pred_alignment2).to_csv('a2.csv')

#alignment with unfolding with updating ids
pt.utils.update_note_ids_after_unfolding(best_match_score_part)
sna3 = best_match_score_part.note_array()
sdm3 = pa.AutomaticNoteMatcher()
pred_alignment3 = sdm3(sna3, pna)
pd.DataFrame(pred_alignment3).to_csv('a3.csv')

alignment2 contains some (~50) duplicate score note IDs for piece K310-2. The score was unfolded but its IDs were not updated, so this score contains several hundred (unplayed) duplicate notes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.