diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..dbeb047e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement via +[email](mailto:carlos_eduardo.cancino_chacon@jku.at). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/LICENSE b/LICENSE index 261eeb9e..475d55dd 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2022, Maarten Grachten, Carlos Cancino-Chacón, Silvan Peter, Emmanouil Karystinaios, Francesco Foscarin Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 956310c5..5367181e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ [![Pypi Package](https://badge.fury.io/py/partitura.svg)](https://badge.fury.io/py/partitura) [![Unittest Status](https://github.com/CPJKU/partitura/workflows/Partitura%20Unittests/badge.svg)](https://github.com/CPJKU/partitura/actions?query=workflow%3A%22Partitura+Unittests%22) [![CodeCov Status](https://codecov.io/gh/CPJKU/partitura/branch/develop/graph/badge.svg?token=mnZ234sGSA)](https://codecov.io/gh/CPJKU/partitura) - +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) @@ -40,11 +40,14 @@ the package: ```python import partitura as pt my_xml_file = pt.EXAMPLE_MUSICXML -part = pt.load_score(my_xml_file) +score = pt.load_score(my_xml_file) ``` -The following shows the contents of the part: +The partitura `load_score` function will import any score format, i.e. (Musicxml, Kern, MIDI or MEI) to a `partitura.Score` object. +The score object will contain all the information in the score, including the score parts. +The following shows the contents of the first part of the score: ```python +part = score.parts[0] print(part.pretty()) ``` Output: @@ -136,7 +139,7 @@ print(beat_map(pianoroll[:, 1])) ``` -The following commands save the part to MIDI and MusicXML, respectively: +The following commands save the part to MIDI and MusicXML, or export it as a WAV file (using [additive synthesis](https://en.wikipedia.org/wiki/Additive_synthesis)), respectively: ```python # Save Score MIDI to file. @@ -144,6 +147,9 @@ pt.save_score_midi(part, 'mypart.mid') # Save Score MusicXML to file. pt.save_musicxml(part, 'mypart.musicxml') + +# Save as audio file using additive synthesis +pt.save_wav(part, 'mypart.wav') ``` @@ -157,7 +163,7 @@ For **MusicXML** files do: ```python import partitura as pt my_xml_file = pt.EXAMPLE_MUSICXML -part = pt.load_musicxml(my_xml_file) +score = pt.load_musicxml(my_xml_file) ``` For **Kern** files do: @@ -165,7 +171,7 @@ For **Kern** files do: ```python import partitura as pt my_kern_file = pt.EXAMPLE_KERN -part = pt.load_kern(my_kern_file) +score = pt.load_kern(my_kern_file) ``` For **MEI** files do: @@ -173,7 +179,7 @@ For **MEI** files do: ```python import partitura as pt my_mei_file = pt.EXAMPLE_MEI -part = pt.load_mei(my_mei_file) +score = pt.load_mei(my_mei_file) ``` @@ -182,7 +188,7 @@ One can also import any of the above formats by just using: ```python import partitura as pt any_score_format_path = pt.EXAMPLE_MUSICXML -part = pt.load_score(any_score_format_path) +score = pt.load_score(any_score_format_path) ``` diff --git a/docs/Tutorial/notebook.ipynb b/docs/Tutorial/notebook.ipynb new file mode 100644 index 00000000..f8aa74ca --- /dev/null +++ b/docs/Tutorial/notebook.ipynb @@ -0,0 +1,1601 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "blessed-heavy", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "id": "legitimate-immigration", + "metadata": {}, + "source": [ + "# An Introduction to Symbolic Music Processing with Partitura\n", + "\n", + "Partitura is python 3 package for symbolic music processing developed and maintained at OFAI Vienna / CP JKU Linz (and other contributors). It's inteded to give a lightweight musical part representation that makes many score properties easily accessible for a variety of tasks. Furthermore it's a very useful I/O utility to parse computer formats of symbolic music. " + ] + }, + { + "cell_type": "markdown", + "id": "nonprofit-communication", + "metadata": { + "id": "3tvQmcSB7rrL" + }, + "source": [ + "## 1. Install and import\n", + "\n", + "Partitura is available in github https://github.com/CPJKU/partitura\n", + "\n", + "You can install it with `pip install partitura`.\n", + "\n", + "However if you are interested in features that still have to be officially released, it's better to install the develop branch." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "facial-quarterly", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PeabdL1k7YC4", + "outputId": "fcb7d1be-27a1-4c79-c5d3-8cbfa54cae44", + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: partitura in /home/manos/Desktop/JKU/codes/partitura (1.0.0)\r\n", + "Requirement already satisfied: numpy in /home/manos/miniconda3/envs/partitura/lib/python3.8/site-packages (from partitura) (1.21.2)\r\n", + "Requirement already satisfied: scipy in /home/manos/miniconda3/envs/partitura/lib/python3.8/site-packages (from partitura) (1.7.1)\r\n", + "Requirement already satisfied: lxml in /home/manos/miniconda3/envs/partitura/lib/python3.8/site-packages (from partitura) (4.6.3)\r\n", + "Requirement already satisfied: lark-parser in /home/manos/miniconda3/envs/partitura/lib/python3.8/site-packages (from partitura) (0.12.0)\r\n", + "Requirement already satisfied: xmlschema in /home/manos/miniconda3/envs/partitura/lib/python3.8/site-packages (from partitura) (1.8.0)\r\n", + "Requirement already satisfied: mido in /home/manos/miniconda3/envs/partitura/lib/python3.8/site-packages (from partitura) (1.2.10)\r\n", + "Requirement already satisfied: elementpath<3.0.0,>=2.2.2 in /home/manos/miniconda3/envs/partitura/lib/python3.8/site-packages (from xmlschema->partitura) (2.3.2)\r\n", + "fatal: destination path 'partitura_tutorial' already exists and is not an empty directory.\r\n" + ] + } + ], + "source": [ + "# Install partitura\n", + "! pip install partitura\n", + " \n", + "# To be able to access helper modules in the repo for this tutorial\n", + "# (not necessary if the jupyter notebook is run locally instead of google colab)\n", + "!git clone https://github.com/CPJKU/partitura_tutorial.git\n", + " \n", + "import sys, os\n", + "sys.path.insert(0, os.path.join(os.getcwd(), \"partitura_tutorial\", \"content\"))\n", + "sys.path.insert(0,'/content/partitura_tutorial/content')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "impressed-principle", + "metadata": {}, + "outputs": [], + "source": [ + "import glob\n", + "import partitura as pt\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "id": "toxic-italian", + "metadata": { + "id": "CX8wCxyK7emp" + }, + "source": [ + "#### Dataset for this tutorial\n", + "\n", + "In this tutorial we are going to use the [Vienna 4x22 Corpus](https://repo.mdw.ac.at/projects/IWK/the_vienna_4x22_piano_corpus/index.html) which consists of performances of 4 classical piano pieces, which have been aligned to their corresponding scores.\n", + "\n", + "The dataset contains:\n", + "\n", + "* Scores in MusicXML format (4 scores)\n", + "* Performances in MIDI files (88 in total, 22 performances per piece, each by a different pianist)\n", + "* Score to performance alignments in Match file format (88 in total one file per performance)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "photographic-profession", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "Output()", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "6918ecbb7839408cb384594317cddb27" + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# setup the dataset\n", + "from load_data import init_dataset\n", + "DATASET_DIR = init_dataset()\n", + "MUSICXML_DIR = os.path.join(DATASET_DIR, 'musicxml')\n", + "MIDI_DIR = os.path.join(DATASET_DIR, 'midi')\n", + "MATCH_DIR = os.path.join(DATASET_DIR, 'match')" + ] + }, + { + "cell_type": "markdown", + "id": "valued-helena", + "metadata": {}, + "source": [ + "## 2. Loading and Exporting Files\n", + "\n", + "One of the main use cases of partitura is to load and export common symbolic music formats." + ] + }, + { + "cell_type": "markdown", + "id": "sonic-better", + "metadata": {}, + "source": [ + "### Supported Formats\n", + "\n", + "#### Reading\n", + "\n", + "##### Symbolic Scores\n", + "\n", + "These methods return a `Part`, a `PartGroup` or a list of `Part` objects.\n", + "\n", + "|Format| Method|Notes|\n", + "|:---|:---|:---|\n", + "|MusicXML| `partitura.load_musicxml`| |\n", + "|MIDI| `partitura.load_score_midi`|Pitch spelling, key signature (optional) and other information is inferred with methods in `partitura.musicanalysis`. \n", + "|MEI| `partitura.load_mei`|\n", + "|Humdrum Kern| `partitura.load_kern`|\n", + "|MuseScore|`partitura.load_via_musescore`| Requires [MuseScore](https://musescore.org/en). Loads all formats supported by MuseScore. Support on Windows is still untested.\n", + "\n", + "##### Symbolic Performances\n", + "\n", + "These methods return a `PerformedPart`.\n", + "\n", + "|Format| Method|Notes|\n", + "|:---|:---|:---|\n", + "|MIDI|`partitura.load_performance_midi`| Loads MIDI file as a performance, including track, channel and program information. Time signature and tempo information are only used to compute the time of the MIDI messages in seconds. Key signature information is ignored\n", + "\n", + "##### Alignments\n", + "\n", + "These methods return score-to-performance alignment (discussed below).\n", + "\n", + "|Format| Method|Notes|\n", + "|:---|:---|:---|\n", + "|Match file| `partitura.load_match`| Returns alignment, a performance as `PerformedPart` and optionally a `Part`. See usage below.\n", + "|Nakamura et al. corresp file | `partitura.load_nakamuracorresp`|\n", + "|Nakamura et al. match file| `partitura.load_nakamuramatch`|\n", + "\n", + "#### Writing\n", + "\n", + "##### Symbolic Scores\n", + "\n", + "Support for MEI and Humdrum Kern is coming!\n", + "\n", + "|Format| Method|Notes|\n", + "|:---|:---|:---|\n", + "|MusicXML| `partitura.save_musicxml`|\n", + "|MIDI| `partitura.save_score_midi`| Includes Key signature, time signature and tempo information.\n", + "\n", + "##### Symbolic Performances\n", + "|Format| Method|Notes|\n", + "|:---|:---|:---|\n", + "|MIDI|`partitura.save_performance_midi`| Does not include key signature or time signature information\n", + "\n", + "##### Alignments\n", + "\n", + "A companion library for music alignment is in preparation!\n", + "\n", + "|Format| Method|Notes|\n", + "|:---|:---|:---|\n", + "|Match file| `partitura.save_match`| \n" + ] + }, + { + "cell_type": "markdown", + "id": "dd98b602", + "metadata": {}, + "source": [ + "## 3. Internal Representations\n", + "\n", + "### 3.1 The Part Object\n", + "\n", + "The ```part``` object is the central object of partitura. It contains a score.\n", + "- it is a timeline object\n", + "- time is measured in divs\n", + "- its elements are timed objects, i.e. they have a starting time and an ending time\n", + "- external score files are loaded into a part\n", + "- parts can be exported into score files\n", + "- it contains many useful methods related to its properties\n", + "\n", + "Here's a visual representation of the ```part``` object representing the first measure of Chopin's Nocturne Op. 9 No. 2" + ] + }, + { + "cell_type": "markdown", + "id": "5754b952", + "metadata": {}, + "source": [ + "![Timeline_chopin2.png](https://github.com/CPJKU/partitura_tutorial/raw/main/static/Timeline_chopin2.png)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c9179e78", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Part id=\"P1\" name=\"Piano\"\n", + " │\n", + " ├─ TimePoint t=0 quarter=12\n", + " │ │\n", + " │ └─ starting objects\n", + " │ │\n", + " │ ├─ 0--48 Measure number=1\n", + " │ ├─ 0--48 Note id=n01 voice=1 staff=2 type=whole pitch=A4\n", + " │ ├─ 0--48 Page number=1\n", + " │ ├─ 0--24 Rest id=r01 voice=2 staff=1 type=half\n", + " │ ├─ 0--48 System number=1\n", + " │ └─ 0-- TimeSignature 4/4\n", + " │\n", + " ├─ TimePoint t=24 quarter=12\n", + " │ │\n", + " │ ├─ ending objects\n", + " │ │ │\n", + " │ │ └─ 0--24 Rest id=r01 voice=2 staff=1 type=half\n", + " │ │\n", + " │ └─ starting objects\n", + " │ │\n", + " │ ├─ 24--48 Note id=n02 voice=2 staff=1 type=half pitch=C5\n", + " │ └─ 24--48 Note id=n03 voice=2 staff=1 type=half pitch=E5\n", + " │\n", + " └─ TimePoint t=48 quarter=12\n", + " │\n", + " └─ ending objects\n", + " │\n", + " ├─ 0--48 Measure number=1\n", + " ├─ 0--48 Note id=n01 voice=1 staff=2 type=whole pitch=A4\n", + " ├─ 24--48 Note id=n02 voice=2 staff=1 type=half pitch=C5\n", + " ├─ 24--48 Note id=n03 voice=2 staff=1 type=half pitch=E5\n", + " ├─ 0--48 Page number=1\n", + " └─ 0--48 System number=1\n" + ] + } + ], + "source": [ + "path_to_musicxml = pt.EXAMPLE_MUSICXML\n", + "part = pt.load_musicxml(path_to_musicxml)[0]\n", + "print(part.pretty())" + ] + }, + { + "cell_type": "markdown", + "id": "874a18d5", + "metadata": {}, + "source": [ + "![score_example.png](https://github.com/CPJKU/partitura_tutorial/raw/main/static/score_example.png)" + ] + }, + { + "cell_type": "markdown", + "id": "c5bae1ed", + "metadata": {}, + "source": [ + "### Part Notes\n", + "\n", + "Each ```part``` object contains a list notes. Notes inherit from the ```TimedObject``` class. Like all ```TimedObjects``` they contain a (possibly coincident) start time and end time, encoded as ```TimePoint``` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "423aac6a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "[,\n ,\n ]" + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "part.notes" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "0a929369", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "['__class__',\n '__delattr__',\n '__dict__',\n '__dir__',\n '__doc__',\n '__eq__',\n '__format__',\n '__ge__',\n '__getattribute__',\n '__gt__',\n '__hash__',\n '__init__',\n '__init_subclass__',\n '__le__',\n '__lt__',\n '__module__',\n '__ne__',\n '__new__',\n '__reduce__',\n '__reduce_ex__',\n '__repr__',\n '__setattr__',\n '__sizeof__',\n '__str__',\n '__subclasshook__',\n '__weakref__',\n '_ref_attrs',\n '_sym_dur',\n 'alter',\n 'alter_sign',\n 'articulations',\n 'beam',\n 'doc_order',\n 'duration',\n 'duration_from_symbolic',\n 'duration_tied',\n 'end',\n 'end_tied',\n 'fermata',\n 'id',\n 'iter_chord',\n 'midi_pitch',\n 'octave',\n 'ornaments',\n 'replace_refs',\n 'slur_starts',\n 'slur_stops',\n 'staff',\n 'start',\n 'step',\n 'symbolic_duration',\n 'tie_next',\n 'tie_next_notes',\n 'tie_prev',\n 'tie_prev_notes',\n 'tuplet_starts',\n 'tuplet_stops',\n 'voice']" + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(part.notes[0])" + ] + }, + { + "cell_type": "markdown", + "id": "c2287849", + "metadata": {}, + "source": [ + "You can create notes (without timing information) and then add it to a part by specifying start and end times (in divs!). Use each note object only once! You can remove notes from a part." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "2a8293c9", + "metadata": {}, + "outputs": [], + "source": [ + "a_new_note = pt.score.Note(id='n04', step='A', octave=4, voice=1)\n", + "part.add(a_new_note, start=3, end=15)\n", + "# print(part.pretty())" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "eba2fa93", + "metadata": {}, + "outputs": [], + "source": [ + "part.remove(a_new_note)\n", + "# print(part.pretty())" + ] + }, + { + "cell_type": "markdown", + "id": "a8649483", + "metadata": {}, + "source": [ + "### Converting from divs to musical units and back\n", + "\n", + "Integer divs are useful for encoding scores but unwieldy for human readers. Partitura offers a variety of ```*unit*_maps``` from the timeline unit \"div\" to musical units such as \"beats\" (in two different readings) or \"quarters\". For the inverse operation the corresponding ```inv_*unit*_map``` exist as well. Quarter to div ratio is a fixed value for a ```part``` object, but units like beats might change with time signature, so these ```maps``` are implemented as ```part``` methods.\n", + "\n", + "Let's look at how to get the ending position in beats of the last note in our example ```part```." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e95eb0f7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "array(4.)" + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "part.beat_map(part.notes[0].end.t)" + ] + }, + { + "cell_type": "markdown", + "id": "6a2b7c10", + "metadata": {}, + "source": [ + "Some musical information such as key and time signature is valid for a segment of the score but only encoded in one location. To retrieve the \"currently active\" time or key signature at any score position, ```maps``` are available too." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "05346a03", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "array([4., 4., 4.])" + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "part.time_signature_map(part.notes[0].end.t)" + ] + }, + { + "cell_type": "markdown", + "id": "bf1d6ae9", + "metadata": {}, + "source": [ + "### Iterating over arbitrary musical objects in a part\n", + "\n", + "The ```part``` class contains a central method ```iter_all``` to iterate over all instances of the ```TimedObject``` class or its subclasses of a part. The ```iter_all``` method returns an iterator and takes five optional parameters: \n", + "- A ```TimedObject``` subclass whose instances are returned. You can find them all in the partitura/partitura/score.py file. Default is all classes.\n", + "- A ```include_subclasses``` flag. If true, instances of subclasses are returned too. E.g. ``` part.iter_all(pt.score.TimedObject, include_subclasses=True)``` returns all objects or ```part.iter_all(pt.score.GenericNote, include_subclasses=True)``` returns all notes (grace notes, standard notes)\n", + "- A start time in divs to specify the search interval (default is beginning of the part)\n", + "- An end time in divs to specify the search interval (default is end of the part)\n", + "- A ```mode``` parameter to define whether to search for starting or ending objects, defaults to starting." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "74943a93", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0--48 Measure number=1\n" + ] + } + ], + "source": [ + "for measure in part.iter_all(pt.score.Measure):\n", + " print(measure)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "6cbfd044", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0--48 Note id=n01 voice=1 staff=2 type=whole pitch=A4\n", + "0--24 Rest id=r01 voice=2 staff=1 type=half\n" + ] + } + ], + "source": [ + "for note in part.iter_all(pt.score.GenericNote, include_subclasses=True, start=0, end=24):\n", + " print(note)" + ] + }, + { + "cell_type": "markdown", + "id": "d1455a5f", + "metadata": {}, + "source": [ + "### Example: Adding a new measure and a note at its downbeat\n", + "\n", + "Let's use class retrieval, time mapping, and object creation together and add a new measure with a single beat-length note at its downbeat. This code works even if you know nothing about the underlying score." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "fe430921", + "metadata": {}, + "outputs": [], + "source": [ + "# figure out the last measure position, time signature and beat length in divs\n", + "measures = [m for m in part.iter_all(pt.score.Measure)]\n", + "last_measure_number = measures[-1].number\n", + "append_measure_start = measures[-1].end.t \n", + "Last_measure_ts = part.time_signature_map(append_measure_start)\n", + "\n", + "Last_measure_ts = part.time_signature_map(append_measure_start)\n", + "one_beat_in_divs_at_the_end = append_measure_start - part.inv_beat_map(part.beat_map(append_measure_start)-1)\n", + "append_measure_end = append_measure_start + one_beat_in_divs_at_the_end*Last_measure_ts[0]\n", + "\n", + "# add a measure\n", + "a_new_measure = pt.score.Measure(number = last_measure_number+1)\n", + "part.add(a_new_measure, start=append_measure_start, end=append_measure_end)\n", + "# add a note\n", + "a_new_note = pt.score.Note(id='n04', step='A', octave=4, voice=1)\n", + "part.add(a_new_note, start=append_measure_start, end=append_measure_start+one_beat_in_divs_at_the_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "f9d738a5", + "metadata": {}, + "outputs": [], + "source": [ + "# print(part.pretty())" + ] + }, + { + "cell_type": "markdown", + "id": "129e4c8b", + "metadata": {}, + "source": [ + "### 3.2 The PerformedPart Object\n", + "\n", + "The ```PerformedPart``` class is a wrapper for MIDI files. Its structure is much simpler:\n", + "- a notes property that consists of list of MIDI notes as dictionaries\n", + "- a controls property that consists of list of MIDI CC messages\n", + "- some more utility methods and properties" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "5d82a340", + "metadata": {}, + "outputs": [], + "source": [ + "path_to_midifile = pt.EXAMPLE_MIDI\n", + "performedpart = pt.load_performance_midi(path_to_midifile)[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "4e3090d9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "[{'midi_pitch': 69,\n 'note_on': 0.0,\n 'note_off': 2.0,\n 'track': 0,\n 'channel': 1,\n 'velocity': 64,\n 'id': 'n0',\n 'sound_off': 2.0},\n {'midi_pitch': 72,\n 'note_on': 1.0,\n 'note_off': 2.0,\n 'track': 0,\n 'channel': 2,\n 'velocity': 64,\n 'id': 'n1',\n 'sound_off': 2.0},\n {'midi_pitch': 76,\n 'note_on': 1.0,\n 'note_off': 2.0,\n 'track': 0,\n 'channel': 2,\n 'velocity': 64,\n 'id': 'n2',\n 'sound_off': 2.0}]" + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "performedpart.notes" + ] + }, + { + "cell_type": "markdown", + "id": "2bf8c482", + "metadata": {}, + "source": [ + "### 3.3 Tiny example with cats on keyboards" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "d6eb12f2", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np \n", + "\n", + "def addnote(midipitch, part, voice, start, end, idx):\n", + " \"\"\"\n", + " adds a single note by midipitch to a part\n", + " \"\"\"\n", + " offset = midipitch%12\n", + " octave = int(midipitch-offset)/12\n", + " name = [(\"C\",0),\n", + " (\"C\",1),\n", + " (\"D\",0),\n", + " (\"D\",1),\n", + " (\"E\",0),\n", + " (\"F\",0),\n", + " (\"F\",1),\n", + " (\"G\",0),\n", + " (\"G\",1),\n", + " (\"A\",0),\n", + " (\"A\",1),\n", + " (\"B\",0)]\n", + " # print( id, start, end, offset)\n", + " step, alter = name[int(offset)]\n", + " part.add(pt.score.Note(id='n{}'.format(idx), step=step, \n", + " octave=int(octave), alter=alter, voice=voice, staff=str((voice-1)%2+1)), \n", + " start=start, end=end)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "572e856c", + "metadata": {}, + "outputs": [], + "source": [ + "l = 200\n", + "p = pt.score.Part('CoK', 'Cat on Keyboard', quarter_duration=8)\n", + "dur = np.random.randint(1,20, size=(4,l+1))\n", + "ons = np.cumsum(dur, axis = 1)\n", + "pitch = np.row_stack((np.random.randint(20,40, size=(1,l+1)),\n", + " np.random.randint(60,80, size=(1,l+1)),\n", + " np.random.randint(40,60, size=(1,l+1)),\n", + " np.random.randint(40,60, size=(1,l+1))\n", + " ))" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "f9f03a50", + "metadata": {}, + "outputs": [], + "source": [ + "for k in range(l):\n", + " for j in range(4):\n", + " addnote(pitch[j,k], p, j+1, ons[j,k], ons[j,k]+dur[j,k+1], \"v\"+str(j)+\"n\"+str(k))" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "09fb6b45", + "metadata": {}, + "outputs": [], + "source": [ + "p.add(pt.score.TimeSignature(4, 4), start=0)\n", + "p.add(pt.score.Clef(1, \"G\", line = 3, octave_change=0),start=0)\n", + "p.add(pt.score.Clef(2, \"G\", line = 3, octave_change=0),start=0)\n", + "pt.score.add_measures(p)\n", + "pt.score.tie_notes(p)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "834582d5", + "metadata": {}, + "outputs": [], + "source": [ + "# pt.save_score_midi(p, \"CatPerformance.mid\", part_voice_assign_mode=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "006f02ed", + "metadata": {}, + "outputs": [], + "source": [ + "# pt.save_musicxml(p, \"CatScore.xml\")" + ] + }, + { + "cell_type": "markdown", + "id": "identical-gathering", + "metadata": {}, + "source": [ + "## 4. Extracting Information from Scores and Performances\n", + "\n", + "For many MIR tasks we need to extract specific information out of scores or performances. \n", + "Two of the most common representations are **note arrays** and **piano rolls**. \n", + "\n", + "**Note that there is some overlap in the way that these terms are used.**\n", + "\n", + "Partitura provides convenience methods to extract these common features in a few lines!" + ] + }, + { + "cell_type": "markdown", + "id": "ordinary-psychology", + "metadata": {}, + "source": [ + "### 4.1 Note Arrays\n", + "\n", + "A **note array** is a 2D array in which each row represents a note in the score/performance and each column represents different attributes of the note.\n", + "\n", + "In partitura, note arrays are [structured numpy arrays](https://numpy.org/devdocs/user/basics.rec.html), which are ndarrays in which each \"column\" has a name, and can be of different datatypes. \n", + "This allows us to hold information that can be represented as integers (MIDI pitch/velocity), floating point numbers (e.g., onset time) or strings (e.g., note ids). \n", + "\n", + "In this tutorial we are going to cover 3 main cases\n", + "\n", + "* Getting a note array from `Part` and `PerformedPart` objects\n", + "* Extra information and alternative ways to generate a note array\n", + "* Creating a custom note array from scratch from a `Part` object\n", + "\n", + "\n", + "#### 4.1.1. Getting a note array from `Part` and `PerformedPart` objects\n", + "\n", + "##### Getting a note array from `Part` objects" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "first-basin", + "metadata": {}, + "outputs": [], + "source": [ + "# Note array from a score\n", + "\n", + "# Path to the MusicXML file\n", + "score_fn = os.path.join(MUSICXML_DIR, 'Chopin_op38.musicxml')\n", + "\n", + "# Load the score into a `Part` object\n", + "score_part = pt.load_musicxml(score_fn)\n", + "\n", + "# Get note array.\n", + "score_note_array = score_part.note_array()" + ] + }, + { + "cell_type": "markdown", + "id": "looking-whole", + "metadata": {}, + "source": [ + "It is that easy!" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "alternate-coordinate", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(-4., 1., -2. , 0.5, 0, 8, 60, 4, 'n2', 16)\n", + " (-4., 1., -2. , 0.5, 0, 8, 72, 1, 'n1', 16)\n", + " (-3., 2., -1.5, 1. , 8, 16, 60, 4, 'n4', 16)\n", + " (-3., 2., -1.5, 1. , 8, 16, 72, 1, 'n3', 16)\n", + " (-1., 1., -0.5, 0.5, 24, 8, 60, 4, 'n6', 16)\n", + " (-1., 1., -0.5, 0.5, 24, 8, 72, 1, 'n5', 16)\n", + " ( 0., 2., 0. , 1. , 32, 16, 60, 4, 'n8', 16)\n", + " ( 0., 2., 0. , 1. , 32, 16, 72, 1, 'n7', 16)\n", + " ( 2., 1., 1. , 0.5, 48, 8, 60, 4, 'n10', 16)\n", + " ( 2., 1., 1. , 0.5, 48, 8, 72, 1, 'n9', 16)]\n" + ] + } + ], + "source": [ + "# Lets see the first notes in this note array\n", + "print(score_note_array[:10])" + ] + }, + { + "cell_type": "markdown", + "id": "toxic-publicity", + "metadata": {}, + "source": [ + "![example_note_array-2.png](https://raw.githubusercontent.com/CPJKU/partitura_tutorial/main/static/example_note_array.png)\n", + "\n", + "By default, Partitura includes some of the most common note-level information in the note array:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "subtle-millennium", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('onset_beat', 'duration_beat', 'onset_quarter', 'duration_quarter', 'onset_div', 'duration_div', 'pitch', 'voice', 'id', 'divs_pq')\n" + ] + } + ], + "source": [ + "print(score_note_array.dtype.names)" + ] + }, + { + "cell_type": "markdown", + "id": "exact-practice", + "metadata": {}, + "source": [ + "* `onset_beat` is the onset time in beats (as indicated by the time signature). In partitura, negative onset times in beats represent pickup measures. Onset time 0 is the start of the first measure.\n", + "* `duration_beat` is the duration of the note in beats\n", + "* `onset_quarter` is the onset time of the note in quarters (independent of the time signature). Similarly to onset time in beats, negative onset times in quarters represent pickup measures and onset time 0 is the start of the first measure.\n", + "* `duration_quarter`is the duration of the note in quarters\n", + "* `onset_div` is the onset of the note in *divs*, which is generally a number that allows to represent the note position and duration losslessly with integers. In contrast to onset time in beats or quarters, onset time in divs always start at 0 at the first \"element\" in the score (which might not necessarily be a note).\n", + "* `duration_div` is the duration of the note in divs.\n", + "* `pitch` is the MIDI pitch (MIDI note number) of the note\n", + "* `voice` is the voice of the note (in polyphonic music, where there can be multiple notes at the same time)\n", + "* `id` is the note id (as appears in MusicXML or MEI formats)" + ] + }, + { + "cell_type": "markdown", + "id": "compressed-baseball", + "metadata": {}, + "source": [ + "##### Getting a note array from a `PerformedPart`\n", + "\n", + "In a similar way, we can obtain a note array from a MIDI file in a few lines" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "passing-lending", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/manos/Desktop/JKU/codes/partitura/partitura/io/importmidi.py:128: UserWarning: change of Tempo to mpq = 500000 and resulting seconds per tick = 0.000125at time: 0.0\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "# Note array from a performance\n", + "\n", + "# Path to the MIDI file\n", + "performance_fn = os.path.join(MIDI_DIR, 'Chopin_op38_p01.mid')\n", + "\n", + "# Loading the file to a PerformedPart\n", + "performance_part = pt.load_performance_midi(performance_fn)\n", + "\n", + "# Get note array!\n", + "performance_note_array = performance_part.note_array()" + ] + }, + { + "cell_type": "markdown", + "id": "bright-equity", + "metadata": {}, + "source": [ + "Since performances contain have other information not included in scores, the default fields in the note array are a little bit different:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "pointed-stupid", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('onset_sec', 'duration_sec', 'pitch', 'velocity', 'track', 'channel', 'id')\n" + ] + } + ], + "source": [ + "print(performance_note_array.dtype.names)" + ] + }, + { + "cell_type": "markdown", + "id": "cathedral-generator", + "metadata": {}, + "source": [ + "* `onset_sec` is the onset time of the note in seconds. Onset time in seconds is always $\\geq 0$ (otherwise, the performance would violate the laws of physics ;)\n", + "* `duration_sec` is the duration of the note in seconds\n", + "* `pitch` is the MIDI pitch\n", + "* `velocity` is the MIDI velocity\n", + "* `track` is the track number in the MIDI file\n", + "* `channel` is the channel in the MIDI file\n", + "* `id` is the ID of the notes (automatically generated for MIDI file according to onset time)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "subject-reducing", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(5.6075 , 5.5025 , 72, 37, 1, 0, 'n0')\n", + " (5.63375, 5.47625, 60, 27, 1, 0, 'n1')\n", + " (6.07 , 5.04 , 72, 45, 1, 0, 'n2')\n", + " (6.11125, 4.99875, 60, 26, 1, 0, 'n3')\n", + " (6.82625, 4.28375, 60, 39, 1, 0, 'n4')]\n" + ] + } + ], + "source": [ + "print(performance_note_array[:5])" + ] + }, + { + "cell_type": "markdown", + "id": "naval-prescription", + "metadata": {}, + "source": [ + "We can also create a `PerformedPart` directly from a note array" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "spread-performer", + "metadata": {}, + "outputs": [], + "source": [ + "note_array = np.array(\n", + " [(60, 0, 2, 40),\n", + " (65, 0, 1, 15),\n", + " (67, 0, 1, 72),\n", + " (69, 1, 1, 90),\n", + " (66, 2, 1, 80)],\n", + " dtype=[(\"pitch\", \"i4\"),\n", + " (\"onset_sec\", \"f4\"),\n", + " (\"duration_sec\", \"f4\"),\n", + " (\"velocity\", \"i4\"),\n", + " ]\n", + ")\n", + "\n", + "# Note array to `PerformedPart`\n", + "performed_part = pt.performance.PerformedPart.from_note_array(note_array)" + ] + }, + { + "cell_type": "markdown", + "id": "catholic-dealer", + "metadata": {}, + "source": [ + "We can then export the `PerformedPart` to a MIDI file!" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "changed-check", + "metadata": {}, + "outputs": [], + "source": [ + "# export as MIDI file\n", + "pt.save_performance_midi(performed_part, \"example.mid\")" + ] + }, + { + "cell_type": "markdown", + "id": "typical-taxation", + "metadata": {}, + "source": [ + "#### 4.1.2. Extra information and alternative ways to generate a note array\n", + "\n", + "Sometimes we require more information in a note array." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "figured-coordinator", + "metadata": {}, + "outputs": [], + "source": [ + "extended_score_note_array = pt.utils.music.ensure_notearray(\n", + " score_part,\n", + " include_pitch_spelling=True, # adds 3 fields: step, alter, octave \n", + " include_key_signature=True, # adds 2 fields: ks_fifths, ks_mode\n", + " include_time_signature=True, # adds 2 fields: ts_beats, ts_beat_type \n", + " # include_metrical_position=True, # adds 3 fields: is_downbeat, rel_onset_div, tot_measure_div\n", + " include_grace_notes=True # adds 2 fields: is_grace, grace_type\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "vietnamese-pathology", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "('onset_beat',\n 'duration_beat',\n 'onset_quarter',\n 'duration_quarter',\n 'onset_div',\n 'duration_div',\n 'pitch',\n 'voice',\n 'id',\n 'step',\n 'alter',\n 'octave',\n 'is_grace',\n 'grace_type',\n 'ks_fifths',\n 'ks_mode',\n 'ts_beats',\n 'ts_beat_type',\n 'ts_mus_beats',\n 'divs_pq')" + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "extended_score_note_array.dtype.names" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "crude-courage", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[('n2', 'C', 0, 4, -1, 1) ('n1', 'C', 0, 5, -1, 1)\n", + " ('n4', 'C', 0, 4, -1, 1) ('n3', 'C', 0, 5, -1, 1)\n", + " ('n6', 'C', 0, 4, -1, 1) ('n5', 'C', 0, 5, -1, 1)\n", + " ('n8', 'C', 0, 4, -1, 1) ('n7', 'C', 0, 5, -1, 1)\n", + " ('n10', 'C', 0, 4, -1, 1) ('n9', 'C', 0, 5, -1, 1)]\n" + ] + } + ], + "source": [ + "print(extended_score_note_array[['id', \n", + " 'step', \n", + " 'alter', \n", + " 'octave', \n", + " 'ks_fifths', \n", + " 'ks_mode', #'is_downbeat'\n", + " ]][:10])" + ] + }, + { + "cell_type": "markdown", + "id": "greek-failure", + "metadata": {}, + "source": [ + "[//]:![example_extended_note_array_cof.png](https://raw.githubusercontent.com/CPJKU/partitura_tutorial/main/static/example_extended_note_array_cof.png)\n", + "
\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "global-elite", + "metadata": {}, + "source": [ + "#### 4.1.3. Creating a custom note array from scratch from a `Part` object\n", + "\n", + "Sometimes we are interested in other note-level information that is not included in the standard note arrays. \n", + "With partitura we can create such a note array easily!\n", + "\n", + "For example, imagine that we want a note array that includes whether the notes have an accent mark." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "invalid-rhythm", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(0.25, 47, 1) (1.25, 47, 1) (2.25, 47, 1) (3. , 68, 1) (3.25, 47, 1)]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/manos/Desktop/JKU/codes/partitura/partitura/directions.py:533: UserWarning: error parsing \"ritenuto\" (UnexpectedCharacters)\n", + " warnings.warn('error parsing \"{}\" ({})'.format(string, type(e).__name__))\n" + ] + } + ], + "source": [ + "# Path to the MusicXML file\n", + "score_fn = os.path.join(MUSICXML_DIR, 'Chopin_op10_no3.musicxml')\n", + "\n", + "# Load the score into a `Part` object\n", + "score_part = pt.load_musicxml(score_fn)[0]\n", + "\n", + "def get_accent_note_array(part):\n", + " \n", + " fields = [(\"onset_beat\", \"f4\"), \n", + " (\"pitch\", \"i4\"),\n", + " (\"accent\", \"i4\")]\n", + " # Get all notes in the part\n", + " notes = part.notes_tied\n", + " # Beat map (maps divs to score time in beats)\n", + " beat_map = part.beat_map\n", + " N = len(notes)\n", + " note_array = np.zeros(N, dtype=fields)\n", + " for i, n in enumerate(notes):\n", + " # MIDI pitch\n", + " note_array[i]['pitch'] = n.midi_pitch\n", + " # Get the onset time in beats\n", + " note_array[i]['onset_beat'] = beat_map(n.start.t)\n", + " \n", + " # Iterate over articulations in the note\n", + " if n.articulations:\n", + " for art in n.articulations:\n", + " if art == 'accent':\n", + " note_array[i]['accent'] = 1\n", + " return note_array\n", + "\n", + "accent_note_array = get_accent_note_array(score_part)\n", + "\n", + "accented_note_idxs = np.where(accent_note_array['accent'])\n", + "print(accent_note_array[accented_note_idxs][:5])" + ] + }, + { + "cell_type": "markdown", + "id": "after-season", + "metadata": {}, + "source": [ + "
\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "adjusted-fundamental", + "metadata": {}, + "source": [ + "### 4.2 Piano rolls\n", + "\n", + "Piano rolls are 2D matrices that represent pitch and time information. The time represents time steps (at a given resolution), while the pitch axis represents which notes are active at a given time step. We can think of piano rolls as the symbolic equivalent of spectrograms. \n", + "\n", + "#### Extracting a piano roll" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "essential-academy", + "metadata": {}, + "outputs": [], + "source": [ + "# TODO: change the example\n", + "# Path to the MusicXML file\n", + "score_fn = os.path.join(MUSICXML_DIR, 'Chopin_op10_no3.musicxml')\n", + "\n", + "# Load the score\n", + "score_part = pt.load_musicxml(score_fn)\n", + "# compute piano roll\n", + "pianoroll = pt.utils.compute_pianoroll(score_part)" + ] + }, + { + "cell_type": "markdown", + "id": "entire-nitrogen", + "metadata": {}, + "source": [ + "The `compute_pianoroll` method has a few arguments to customize the resulting piano roll" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "massive-monaco", + "metadata": {}, + "outputs": [], + "source": [ + "piano_range = True\n", + "time_unit = 'beat'\n", + "time_div = 10\n", + "pianoroll = pt.utils.compute_pianoroll(\n", + " note_info=score_part, # a `Part`, `PerformedPart` or a note array\n", + " time_unit=time_unit, # beat, quarter, div, sec, etc. (depending on note_info)\n", + " time_div=time_div, # Number of cells per time unit\n", + " piano_range=piano_range # Use range of the piano (88 keys)\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "quality-coast", + "metadata": {}, + "source": [ + "An important thing to remember is that in piano rolls generated by `compute_pianoroll`, rows (the vertical axis) represent the pitch dimension and the columns (horizontal) the time dimension. \n", + "This results in a more intuitive way of plotting the piano roll. \n", + "For other applications the transposed version of this piano roll might be more useful (i.e., rows representing time steps and columns representing pitch information).\n", + "\n", + "Since piano rolls can result in very large matrices where most of the elements are 0, the output of `compute_pianoroll` is a [scipy sparse matrix](https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csc_matrix.html). To convert it to a regular numpy array, we can simply use `pianoroll.toarray()`" + ] + }, + { + "cell_type": "markdown", + "id": "intended-answer", + "metadata": {}, + "source": [ + "Let's plot the piano roll!" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "mature-dylan", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(1, figsize=(20, 10))\n", + "ax.imshow(pianoroll.toarray(), origin=\"lower\", cmap='gray', interpolation='nearest', aspect='auto')\n", + "ax.set_xlabel(f'Time ({time_unit}s/{time_div})')\n", + "ax.set_ylabel('Piano key' if piano_range else 'MIDI pitch')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "funky-tract", + "metadata": {}, + "source": [ + "In some cases, we want to know the \"coordinates\" of each of the notes in the piano roll. The `compute_pianoroll` method includes an option to return " + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "palestinian-owner", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[59 0 4]\n", + " [40 4 12]\n", + " [40 4 6]\n", + " [56 4 6]\n", + " [64 4 8]]\n" + ] + } + ], + "source": [ + "pianoroll, note_indices = pt.utils.compute_pianoroll(score_part, return_idxs=True)\n", + "\n", + "# MIDI pitch, start, end\n", + "print(note_indices[:5])" + ] + }, + { + "cell_type": "markdown", + "id": "economic-denial", + "metadata": {}, + "source": [ + "#### Generating a note array from a piano roll\n", + "\n", + "Partitura also includes a method to generate a note array from a piano roll, which can be used to generate a MIDI file. \n", + "This method would be useful, e.g., for music generation tasks" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "parental-links", + "metadata": {}, + "outputs": [], + "source": [ + "pianoroll = pt.utils.compute_pianoroll(score_part)\n", + "\n", + "new_note_array = pt.utils.pianoroll_to_notearray(pianoroll)\n", + "\n", + "# We can export the note array to a MIDI file\n", + "ppart = pt.performance.PerformedPart.from_note_array(new_note_array)\n", + "\n", + "pt.save_performance_midi(ppart, \"newmidi.mid\")" + ] + }, + { + "cell_type": "markdown", + "id": "floating-madison", + "metadata": {}, + "source": [ + "## 5. Handling Alignment Information (Match files)\n", + "\n", + "### 5.1 Loading Alignments\n", + "An important use case of partitura is to handle symbolic alignment information\n", + "\n", + "**Note that partitura itself does not contain methods for alignment**\n", + "\n", + "Partitura supports 2 formats for encoding score-to-performance alignments\n", + "\n", + "* Our match file format, introduced by Gerhard et al. ;)\n", + " * Datasets including match files: Vienna4x22, Magaloff, Zeilinger, Batik, and soon ASAP!\n", + "* The format introduced by [Nakamura et al. (2017)](https://eita-nakamura.github.io/articles/EN_etal_ErrorDetectionAndRealignment_ISMIR2017.pdf)\n", + "\n", + "Let's load an alignment!\n", + "\n", + "We have two common use cases\n", + "\n", + "* We have both the match file and the symbolic score file (e.g., MusicXML or MEI)\n", + "* We have only the match file (only works for our format!)" + ] + }, + { + "cell_type": "markdown", + "id": "crucial-virus", + "metadata": {}, + "source": [ + "#### 5.1.1. Loading an alignment if we only have a match file\n", + "\n", + "A useful property of match files is that they include information about the **score and the performance**. Therefore, it is possible to create both a `Part` and a `PerformedPart` directly from a match file.\n", + "\n", + "* Match files contain all information included in performances in MIDI files, i.e., a MIDI file could be reconstructed from a match file.\n", + "\n", + "* Match files include all information information about pitch spelling and score position and duration of the notes in the score, as well as time and key signature information, and can encode some note-level markings, like accents. Nevertheless, it is important to note that the score information included in a match file is not necessarily complete. For example, match files do not generally include dynamics or tempo markings." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "rolled-cloud", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_907569/209301002.py:4: DeprecationWarning: `create_part` is deprecated as an argument to `load_match`; use `create_score` instead.\n", + " performed_part, alignment, score_part = pt.load_match(match_fn, create_part=True)\n" + ] + } + ], + "source": [ + "# path to the match\n", + "match_fn = os.path.join(MATCH_DIR, 'Chopin_op10_no3_p01.match')\n", + "# loading a match file\n", + "performed_part, alignment, score_part = pt.load_match(match_fn, create_part=True)" + ] + }, + { + "cell_type": "markdown", + "id": "wooden-looking", + "metadata": {}, + "source": [ + "#### 5.1.2. Loading an alignment if we have both score and match files\n", + "\n", + "In many cases, however, we have access to both the score and match files. Using the original score file has a few advantages:\n", + "\n", + "* It ensures that the score information is correct. Generating a `Part` from a match file involves inferring information for non-note elements (e.g., start and end time of the measures, voice information, clefs, staves, etc.).\n", + "* If we want to load several performances of the same piece, we can load the score only once!\n", + "\n", + "This should be the preferred way to get alignment information!" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "latest-smell", + "metadata": {}, + "outputs": [], + "source": [ + "# path to the match\n", + "match_fn = os.path.join(MATCH_DIR, 'Chopin_op10_no3_p01.match')\n", + "# Path to the MusicXML file\n", + "score_fn = os.path.join(MUSICXML_DIR, 'Chopin_op10_no3.musicxml')\n", + "# Load the score into a `Part` object\n", + "score_part = pt.load_musicxml(score_fn)[0]\n", + "\n", + "# loading a match file\n", + "performed_part, alignment = pt.load_match(match_fn)" + ] + }, + { + "cell_type": "markdown", + "id": "pending-college", + "metadata": {}, + "source": [ + "Score-to-performance alignments are represented by lists of dictionaries, which contain the following keys:\n", + "\n", + "* `label`\n", + "\n", + " * `'match'`: there is a performed note corresponding to a score note\n", + " * `'insertion'`: the performed note does not correspond to any note in the score\n", + " * `'deletion'`: there is no performed note corresponding to a note in the score\n", + " * `'ornament'`: the performed note corresponds to the performance of an ornament (e.g., a trill). These notes are matched to the main note in the score. Not all alignments (in the datasets that we have) include ornamnets! Otherwise, ornaments are just treated as insertions.\n", + "* `score_id`: id of the note in the score (in the `Part` object) (only relevant for matches, deletions and ornaments)\n", + "* `performance_id`: Id of the note in the performance (in the `PerformedPart`) (only relevant for matches, insertions and ornaments)" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "radio-interim", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "[{'label': 'match', 'score_id': 'n1', 'performance_id': 0},\n {'label': 'match', 'score_id': 'n2', 'performance_id': 2},\n {'label': 'match', 'score_id': 'n3', 'performance_id': 3},\n {'label': 'match', 'score_id': 'n4', 'performance_id': 1},\n {'label': 'match', 'score_id': 'n5', 'performance_id': 5},\n {'label': 'match', 'score_id': 'n6', 'performance_id': 4},\n {'label': 'match', 'score_id': 'n7', 'performance_id': 6},\n {'label': 'match', 'score_id': 'n8', 'performance_id': 7},\n {'label': 'match', 'score_id': 'n9', 'performance_id': 8},\n {'label': 'match', 'score_id': 'n10', 'performance_id': 9}]" + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "alignment[:10]" + ] + }, + { + "cell_type": "markdown", + "id": "exact-decrease", + "metadata": {}, + "source": [ + "### 5.2 Getting information from the alignments\n", + "\n", + "Partitura includes a few methods for getting information from the alignments.\n", + "\n", + "Let's start by getting the subset of score notes that have a corresponding performed note" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "published-understanding", + "metadata": {}, + "outputs": [], + "source": [ + "# note array of the score\n", + "snote_array = score_part.note_array()\n", + "# note array of the performance\n", + "pnote_array = performed_part.note_array()\n", + "# indices of the notes that have been matched\n", + "matched_note_idxs = pt.utils.music.get_matched_notes(snote_array, pnote_array, alignment)\n", + "\n", + "# note array of the matched score notes\n", + "matched_snote_array = snote_array[matched_note_idxs[:, 0]]\n", + "# note array of the matched performed notes\n", + "matched_pnote_array = pnote_array[matched_note_idxs[:, 1]]" + ] + }, + { + "cell_type": "markdown", + "id": "alike-doctor", + "metadata": {}, + "source": [ + "#### Comparing tempo curves\n", + "\n", + "In this example, we are going to compare tempo curves of different performances of the same piece. Partitura includes a utility function called `get_time_maps_from_alignment`which creates functions (instances of [`scipy.interpolate.interp1d`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html)) that map score time to performance time (and the other way around)." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "offshore-bridal", + "metadata": {}, + "outputs": [], + "source": [ + "# get all match files\n", + "matchfiles = glob.glob(os.path.join(MATCH_DIR, 'Chopin_op10_no3_p*.match'))\n", + "matchfiles.sort()\n", + "\n", + "# Score time from the first to the last onset\n", + "score_time = np.linspace(snote_array['onset_beat'].min(),\n", + " snote_array['onset_beat'].max(),\n", + " 100)\n", + "# Include the last offset\n", + "score_time_ending = np.r_[\n", + " score_time, \n", + " (snote_array['onset_beat'] + snote_array['duration_beat']).max() # last offset\n", + "]\n", + "\n", + "tempo_curves = np.zeros((len(matchfiles), len(score_time)))\n", + "for i, matchfile in enumerate(matchfiles):\n", + " # load alignment\n", + " performance, alignment = pt.load_match(matchfile)\n", + " ppart = performance[0]\n", + " # Get score time to performance time map\n", + " _, stime_to_ptime_map = pt.utils.music.get_time_maps_from_alignment(\n", + " ppart, score_part, alignment)\n", + " # Compute naïve tempo curve\n", + " performance_time = stime_to_ptime_map(score_time_ending)\n", + " tempo_curves[i,:] = 60 * np.diff(score_time_ending) / np.diff(performance_time)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "brazilian-honey", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(1, figsize=(15, 8))\n", + "color = plt.cm.rainbow(np.linspace(0, 1, len(tempo_curves)))\n", + "for i, tempo_curve in enumerate(tempo_curves):\n", + " ax.plot(score_time, tempo_curve, \n", + " label=f'pianist {i + 1:02d}', alpha=0.4, c=color[i])\n", + "\n", + "# plot average performance\n", + "ax.plot(score_time, tempo_curves.mean(0), label='average', c='black', linewidth=2)\n", + "\n", + "# get starting time of each measure in the score\n", + "measure_times = score_part.beat_map([measure.start.t for measure in score_part.iter_all(pt.score.Measure)])\n", + "# do not include pickup measure\n", + "measure_times = measure_times[measure_times >= 0]\n", + "ax.set_title('Chopin Op. 10 No. 3')\n", + "ax.set_xlabel('Score time (beats)')\n", + "ax.set_ylabel('Tempo (bpm)')\n", + "ax.set_xticks(measure_times)\n", + "plt.legend(frameon=False, bbox_to_anchor = (1.15, .9))\n", + "plt.grid(axis='x')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2372b392", + "metadata": {}, + "source": [ + "## The end of the tutorial, the start of your yet untold adventures in symbolic music processing...\n", + "\n", + "Thank you for trying out partitura! We hope it serves you well. \n", + "\n", + "If you miss a particular functionality or encounter a bug, we appreciate it if you raise an issue on github: https://github.com/CPJKU/partitura/issues" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyNCzhR7KnjsrjKGf/HDyInO", + "include_colab_link": true, + "name": "Partitura tutorial", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/conf.py b/docs/conf.py index 2c901b79..51fa874a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,7 +15,6 @@ import pkg_resources sys.path.insert(0, os.path.abspath("../partitura")) - # The master toctree document. master_doc = "index" @@ -23,18 +22,16 @@ project = "partitura" # copyright = '2019, Maarten Grachten' -author = ( - "Maarten Grachten, Carlos Cancino-Chacón, Silvan Peter, Emmanouil Karystinaios, Francesco Foscarin, Thassilo Gadermaier" -) +author = "Maarten Grachten, Carlos Cancino-Chacón, Silvan Peter, Emmanouil Karystinaios, Francesco Foscarin, Thassilo Gadermaier" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = pkg_resources.get_distribution("partitura").version +version = "1.1.0" # pkg_resources.get_distribution("partitura").version # The full version, including alpha/beta/rc tags. -release = version +release = "1.1.0" # # The full version, including alpha/beta/rc tags # release = pkg_resources.get_distribution("partitura").version @@ -45,7 +42,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "python" # -- General configuration --------------------------------------------------- @@ -70,8 +67,24 @@ "sphinx.ext.viewcode", # 'sphinxcontrib.napoleon', "sphinx.ext.napoleon", + 'nbsphinx', + # 'sphinxcontrib.bibtex', # for bibliographic references + # 'sphinxcontrib.rsvgconverter', # for SVG->PDF conversion in LaTeX output + # 'sphinx_gallery.load_style', # load CSS for gallery (needs SG >= 0.6) + # 'sphinx_last_updated_by_git', #? get "last updated" from Git + # 'sphinx_codeautolink', # automatic links from code to documentation + # 'sphinx.ext.intersphinx', # links to other Sphinx projects (e.g. NumPy) ] +# These projects are also used for the sphinx_codeautolink extension: +intersphinx_mapping = { + 'IPython': ('https://ipython.readthedocs.io/en/stable/', None), + 'matplotlib': ('https://matplotlib.org/', None), + 'numpy': ('https://docs.scipy.org/doc/numpy/', None), + 'pandas': ('https://pandas.pydata.org/docs/', None), + 'python': ('https://docs.python.org/3/', None), +} + # see http://stackoverflow.com/q/12206334/562769 numpydoc_show_class_members = False @@ -100,6 +113,8 @@ html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +else: + html_theme = "default" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/docs/index.rst b/docs/index.rst index cd58660f..56700fbf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,19 +10,19 @@ Partitura documentation :maxdepth: 2 introduction - usage + Tutorial/notebook.ipynb genindex .. _api_reference: .. toctree:: - :maxdepth: 1 + :maxdepth: 2 :caption: API Reference - modules/partitura - modules/partitura.score - modules/partitura.performance - modules/partitura.musicanalysis - modules/partitura.utils + ./modules/partitura + ./modules/partitura.score + ./modules/partitura.performance + ./modules/partitura.musicanalysis + ./modules/partitura.utils diff --git a/docs/introduction.rst b/docs/introduction.rst index 4be095a2..0f6f6af6 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -1,44 +1,67 @@ ============ Introduction ============ - -The principal aim of the `partitura` package is to handle richly structured -musical information as conveyed by modern staff music notation. It provides -a much wider range of possibilities to deal with music than the more -reductive (but very common) pianoroll-oriented approach inspired by the -MIDI standard. - -Specifically, the package allows for representing a variety of information -in musical scores beyond the onset, duration and MIDI pitch numbers of -notes, such as: - -* pitch spellings, -* symbolic duration categories, -* and voicing information. - -Moreover, it supports musical notions that are not note-related, like: - -* measures, -* tempo indications, -* performance directions, -* repeat structures, -* and time/key signatures. - -In addition to handling score information, the package can load MIDI recordings of -performed scores, and alignments between scores and performances. +Partitura is a lightweight Python package for handling the musical information contained in symbolic music formats, +such as musical scores and MIDI performances. The package is built for researchers in the music information research (MIR) field +that need easy access to a large amount of musical information. + +As opposed to audio files, symbolically encoded music +contains explicit note information, and organizes them notes in temporal and organizational structures such as measures, beats, parts, and voices. +It can also explicitly represent dynamics and temporal directives and other high-level musical +features such as time signature, pitch spelling, and key signatures. +While this rich set of musical elements adds useful information that can be leveraged by +systems, it also drastically increases the complexity of encoding and processing symbolic musical +formats. Common formats for storage such as MEI, MusicXML, Humdrum \*\*kern and MIDI +are not ideally suited to be directly used as input in MIR tasks. Therefore, the typical data +processing pipeline starts with parsing the relevant information from those files and putting it +into a convenient data structure. + +Partitura provides easy access to features commonly used in music information retrieval tasks, such as: + +* note arrays : lists of timed pitched events +* pianorolls : 2D time x pitch matrices + +It also support other score elements such +as time and key signatures, performance directives, and repeat structures. + +.. The principal aim of the `partitura` package is to handle richly structured +.. musical information as conveyed by modern staff music notation. It provides +.. a much wider range of possibilities to deal with music than the more +.. reductive (but very common) pianoroll-oriented approach inspired by the +.. MIDI standard. + +.. Specifically, the package allows for representing a variety of information +.. in musical scores beyond the onset, duration and MIDI pitch numbers of +.. notes, such as: + +.. * pitch spellings, +.. * symbolic duration categories, +.. * and voicing information. + +.. Moreover, it supports musical notions that are not note-related, like: + +.. * measures, +.. * tempo indications, +.. * performance directions, +.. * repeat structures, +.. * and time/key signatures. + +.. In addition to handling score information, the package can load MIDI recordings of +.. performed scores, and alignments between scores and performances. Supported file types ==================== -Musical data can be loaded from and saved to `MusicXML` and `MIDI` -files. Furthermore, `partitura` uses `MuseScore `_ +Partitura can load musical scores (in MEI, MusicXML, Humdrum \*\*kern, and MIDI formats) +and MIDI performances. + +Furthermore, `partitura` uses `MuseScore `_ as a backend to load files in other formats, like `MuseScore`, `MuseData`, and `GuitarPro`. This requires a working installation of MuseScore on your computer. -`MEI` format is currently not supported, but support is planned for a future release. Score-performance alignments can be read from different file types by -`partitura`. Firstly it supports reading from the `Matchfile` format used by +`partitura`. Firstly, it supports reading from the `Matchfile` format used by the publicly available `Vienna4x22 piano corpus research dataset `_. Secondly there is read support for `Match` and `Corresp` files produced by @@ -104,12 +127,60 @@ Relation to `music21 `_ The `music21` package has been around since 2008, and is one of the few python packages available for working with symbolic musical data. It is -both more mature and more elaborate than `partitura`. The aims of -`partitura` are different from and more modest than those of `music21`, -which aims to provide a toolkit for computer-aided musicology. Instead, -`partitura` intends to provide a convenient way to work with symbolic -musical data in the context of problems such as musical expression -modeling, or music generation. Although it is not the main aim of the -package to provide music analysis tools, the package does offer -functionality for pitch spelling, voice assignment and key estimation. +both more mature and more elaborate than `partitura` for tasks like creating +and manipulating score information and we suggest using it if +you are working in computational musicology. + +`Partitura` is instead built specifically for people that wants to apply machine +learning and deep learning techniques to symbolic music data. Its focus is mainly +on the extraction of relevant features from symbolic music data, in a fast way +that require a minimal musical knowledge. +Moreover partitura supports MIDI performances and score-to-performances +alignments, that are not handled by music21. + +.. A hybrid music21 and partitura usage is also possible thanks to the music21 import function. +.. For example, you can load a score in music21, modify it, and then use the music21 to partitura converter +.. to get the score features that can be computed by partitura. + +.. `partitura` are different from and more modest than those of `music21`, +.. which aims to provide a toolkit for computer-aided musicology. Instead, +.. `partitura` intends to provide a convenient way to work with symbolic +.. musical data in the context of problems such as musical expression modeling, or music generation. Although it is not the main aim of the package to provide music analysis tools, the package does offer functionality for pitch spelling, voice assignment and key estimation. + +Credits +======= + +Citing Partitura +---------------- + +If you find Partitura useful, we would appreciate if you could cite us! + + +>>> @inproceedings{partitura_mec, + title={{Partitura: A Python Package for Symbolic Music Processing}}, + author={Cancino-Chac\'{o}n, Carlos Eduardo and Peter, Silvan David and Karystinaios, Emmanouil and Foscarin, Francesco and Grachten, Maarten and Widmer, Gerhard}, + booktitle={{Proceedings of the Music Encoding Conference (MEC2022)}}, + address={Halifax, Canada}, + year={2022} +} + + +Acknowledgments +--------------- + +This project receives funding from the European Research Council (ERC) under +the European Union's Horizon 2020 research and innovation programme under grant +agreement No 101019375 `"Whither Music?" `_ + + + +This work has received support from the European Research Council (ERC) under +the European Union’s Horizon 2020 research and innovation programme under grant +agreement No. 670035 project `"Con Espressione" `_ +and the Austrian Science Fund (FWF) under grant P 29840-G26 (project +`Computer-assisted Analysis of Herbert von Karajan's Musical Conducting Style `_ ) + +.. image:: ./images/aknowledge_logo.png + :alt: ERC-FWF Logo. + :align: center diff --git a/docs/modules/partitura.musicanalysis.rst b/docs/modules/partitura.musicanalysis.rst index ddbfc4d6..93765dfc 100644 --- a/docs/modules/partitura.musicanalysis.rst +++ b/docs/modules/partitura.musicanalysis.rst @@ -1,6 +1,6 @@ partitura.musicanalysis ======================= - +.. currentmodule:: partitura.musicanalysis .. automodule:: partitura.musicanalysis :members: :undoc-members: diff --git a/docs/usage.rst b/docs/usage.rst deleted file mode 100644 index c458e59e..00000000 --- a/docs/usage.rst +++ /dev/null @@ -1,585 +0,0 @@ -===== -Usage -===== - -In this Section we demonstrate basic usage of the package. - - -Quick start: Reading note information from a MIDI file -====================================================== - -Before we present more in-depth usage of the package, we cover the common use case of reading note information from a MIDI file. The function :func:`~partitura.midi_to_notearray` does exactly that: It loads the note information from the MIDI file MIDI into a `structured numpy array `_ with attributes onset (in seconds), duration (in seconds), pitch, velocity, and ID (automatically generated). -For the purpose of this example we use a small MIDI file that comes with the `partitura` package. The path to the example MIDI file is stored as :const:`partitura.EXAMPLE_MIDI`. - ->>> import partitura ->>> path_to_midifile = partitura.EXAMPLE_MIDI ->>> note_array = partitura.midi_to_notearray(path_to_midifile) ->>> note_array # doctest: +NORMALIZE_WHITESPACE -array([(0., 2., 69, 64, 0, 1, 'n0'), - (1., 1., 72, 64, 0, 2, 'n1'), - (1., 1., 76, 64, 0, 2, 'n2')], - dtype=[('onset_sec', '>> note_array["onset_sec"] # doctest: +NORMALIZE_WHITESPACE -array([0., 1., 1.], dtype=float32) - -To access further information from MIDI files, such as time/key signatures, and control changes, see `Importing MIDI files`_. - - -Importing MusicXML -================== - -As an example we take a MusicXML file with the following contents: - -.. literalinclude:: ../partitura/assets/score_example.musicxml - :language: xml - -To load the score in python we first import the partitura package: - ->>> import partitura - -For convenience a MusicXML file with the above contents is included in the -package. The path to the file is stored as :const:`partitura.EXAMPLE_MUSICXML`, so -that we load the above score as follows: - ->>> path_to_musicxml = partitura.EXAMPLE_MUSICXML ->>> part = partitura.load_musicxml(path_to_musicxml) - - -Displaying the typeset part -=========================== - -The :func:`partitura.render` function displays the part as a typeset score: - ->>> partitura.render(part) - -.. image:: images/score_example.png - :alt: Score example - :align: center - -This should open an image of the score in the default image viewing -application of your desktop. The function requires that either `MuseScore -`_ or `lilypond `_ is -installed on your computer. - - -Exporting a score to MusicXML -============================= - -The :func:`partitura.save_musicxml` function exports score information to -MusicXML. The following line saves `part` to a file `mypart.musicxml`: - ->>> partitura.save_musicxml(part, 'mypart.musicxml') - - -Viewing the contents of a score -=============================== - -The function :func:`~partitura.load_musicxml` returns the score as a -:class:`~partitura.score.Part` instance. When we print it, it displays its -id and part-name: - ->>> print(part) -Part id="P1" name="Piano" - -To see all of the elements in the part at once, we can call its -:meth:`~partitura.score.Part.pretty` method: - ->>> print(part.pretty()) -Part id="P1" name="Piano" - │ - ├─ TimePoint t=0 quarter=12 - │ │ - │ └─ starting objects - │ │ - │ ├─ 0--48 Measure number=1 - │ ├─ 0--48 Note id=n01 voice=1 staff=2 type=whole pitch=A4 - │ ├─ 0--48 Page number=1 - │ ├─ 0--24 Rest id=r01 voice=2 staff=1 type=half - │ ├─ 0--48 System number=1 - │ └─ 0-- TimeSignature 4/4 - │ - ├─ TimePoint t=24 quarter=12 - │ │ - │ ├─ ending objects - │ │ │ - │ │ └─ 0--24 Rest id=r01 voice=2 staff=1 type=half - │ │ - │ └─ starting objects - │ │ - │ ├─ 24--48 Note id=n02 voice=2 staff=1 type=half pitch=C5 - │ └─ 24--48 Note id=n03 voice=2 staff=1 type=half pitch=E5 - │ - └─ TimePoint t=48 quarter=12 - │ - └─ ending objects - │ - ├─ 0--48 Measure number=1 - ├─ 0--48 Note id=n01 voice=1 staff=2 type=whole pitch=A4 - ├─ 24--48 Note id=n02 voice=2 staff=1 type=half pitch=C5 - ├─ 24--48 Note id=n03 voice=2 staff=1 type=half pitch=E5 - ├─ 0--48 Page number=1 - └─ 0--48 System number=1 - -This reveals that the part has three time points at which one or more musical -objects start or end. At `t=0` there are several starting objects, including a -:class:`~partitura.score.TimeSignature`, :class:`~partitura.score.Measure`, -:class:`~partitura.score.Page`, and :class:`~partitura.score.System`. - - -Extracting note information from a Part -======================================= - -The notes in this part can be accessed through the :attr:`~partitura.score.Part.notes` property: - -.. doctest:: - - >>> part.notes # doctest: +NORMALIZE_WHITESPACE - [, - , - ] - >>> part.notes[0].duration # duration in divs - 48 - -.. - Like note start and end times, durations are integer values that are The unit of these values is specified in MusicXML files by the - `divisions` element, and in MIDI files by the . This element specifies the duration of - a quarter note. The `divisions` value can vary within an MusicXML file, so it is - generally better to work with musical time in beats. - -Alternatively, basic note attributes can be accessed through the :attr:`~partitura.score.Part.note_array` property: - -.. doctest:: - - >>> arr = part.note_array() - >>> arr.dtype # doctest: +NORMALIZE_WHITESPACE - dtype([('onset_beat', '>> for pitch, onset, duration in arr[["pitch", "onset_beat", "duration_beat"]]: -... print(pitch, onset, duration) -69 0.0 4.0 -72 2.0 2.0 -76 2.0 2.0 - - -.. - The part object has a property :attr:`part.beat_map - ` that converts timeline times into beat - times: - - -Iterating over arbitrary musical objects -======================================== - -In the previous Section we used :attr:`part.notes -` to obtain the notes in the part as a list. -This property is a shortcut for the following statement: - -.. doctest:: - - >>> list(part.iter_all(partitura.score.Note)) # doctest: +NORMALIZE_WHITESPACE - [, - , - ] - -That is, we iterate over all objects of class :class:`partitura.score.Note`, and -store them in a list. The :meth:`~partitura.score.Part.iter_all` method can be -used to iterate over objects of arbitrary classes in the part: - ->>> for m in part.iter_all(partitura.score.Measure): -... print(m) -0--48 Measure number=1 - -The :meth:`~partitura.score.Part.iter_all` method has a keyword -`include_subclasses` that indicates that we are also interested in any -subclasses of the specified class. For example, the following statement -iterates over all objects in the part: - ->>> for m in part.iter_all(object, include_subclasses=True): -... print(m) -0--48 Note id=n01 voice=1 staff=2 type=whole pitch=A4 -0--24 Rest id=r01 voice=2 staff=1 type=half -0--48 Page number=1 -0--48 System number=1 -0--48 Measure number=1 -0-- TimeSignature 4/4 -24--48 Note id=n02 voice=2 staff=1 type=half pitch=C5 -24--48 Note id=n03 voice=2 staff=1 type=half pitch=E5 - -This approach is useful for example when we want to retrieve rests in -addition to notes. Since rests and notes are both subclassess of -:class:`GenericNote `, the following works: - ->>> for m in part.iter_all(partitura.score.GenericNote, include_subclasses=True): -... print(m) -0--48 Note id=n01 voice=1 staff=2 type=whole pitch=A4 -0--24 Rest id=r01 voice=2 staff=1 type=half -24--48 Note id=n02 voice=2 staff=1 type=half pitch=C5 -24--48 Note id=n03 voice=2 staff=1 type=half pitch=E5 - -By default, `include_subclasses` is False. - -.. - - -Creating a musical score by hand -================================ - -You can build a musical score from scratch, by creating a :class:`partitura.score.Part` object. We -start by renaming the `partitura.score` module to `score`, for convenience: - ->>> import partitura.score as score - -Then we create an empty part with id 'P0' and name 'My Part' (the name is -optional, the id is mandatory), and a quarter note -duration of 10 units. - ->>> part = score.Part('P0', 'My Part', quarter_duration=10) - -Adding elements to the part is done by the -:meth:`~partitura.score.Part.add` method, which takes a musical element, -a start and an end time. Either of the `start` and `end` arguments can be -omitted, but if both are omitted the method will do nothing. - -We now add a 3/4 time signature at t=0, and three notes. The notes are -instantiated by specifying an (optional) id, pitch information, and an -(optional) voice: - ->>> part.add(score.TimeSignature(3, 4), start=0) ->>> part.add(score.Note(id='n0', step='A', octave=4, voice=1), start=0, end=10) ->>> part.add(score.Note(id='n1', step='C', octave=5, alter=1, voice=2), start=0, end=10) ->>> part.add(score.Note(id='n2', step='C', octave=5, alter=1, voice=2), start=10, end=40) - -Note that the duration of notes is not hard-coded in the Note instances, but -defined implicitly by their start and end times in the part. - -Here's what the part looks like: - ->>> print(part.pretty()) -Part id="P0" name="My Part" - │ - ├─ TimePoint t=0 quarter=10 - │ │ - │ └─ starting objects - │ │ - │ ├─ 0--10 Note id=n0 voice=1 staff=None type=quarter pitch=A4 - │ ├─ 0--10 Note id=n1 voice=2 staff=None type=quarter pitch=C#5 - │ └─ 0-- TimeSignature 3/4 - │ - ├─ TimePoint t=10 quarter=10 - │ │ - │ ├─ ending objects - │ │ │ - │ │ ├─ 0--10 Note id=n0 voice=1 staff=None type=quarter pitch=A4 - │ │ └─ 0--10 Note id=n1 voice=2 staff=None type=quarter pitch=C#5 - │ │ - │ └─ starting objects - │ │ - │ └─ 10--40 Note id=n2 voice=2 staff=None type=half. pitch=C#5 - │ - └─ TimePoint t=40 quarter=10 - │ - └─ ending objects - │ - └─ 10--40 Note id=n2 voice=2 staff=None type=half. pitch=C#5 - -We see that the notes n0, n1, and n2 have been correctly recognized as -quarter, quarter, and dotted half, respectively. - -Let's save the part to MusicXML: - ->>> partitura.save_musicxml(part, 'mypart.musicxml') - -When we look at the contents of `mypart.musicxml`, surprisingly, the `` element is empty: - -.. code-block:: xml - - - - - - - My Part - - - - - -The problem with our newly created part is that it contains no -measures. Since the MusicXML format requires musical elements to be -contained in measures, saving the part to MusicXML omits the objects we -added. - - -Adding measures -=============== - -One option to add measures is to add them by hand like we've added the -notes and time signature. A more convenient alternative is to use the -function :func:`~partitura.score.add_measures`: - ->>> score.add_measures(part) - -This function uses the time signature information in the part to add -measures accordingly: - ->>> print(part.pretty()) -Part id="P0" name="My Part" - │ - ├─ TimePoint t=0 quarter=10 - │ │ - │ └─ starting objects - │ │ - │ ├─ 0--30 Measure number=1 - │ ├─ 0--10 Note id=n0 voice=1 staff=None type=quarter pitch=A4 - │ ├─ 0--10 Note id=n1 voice=2 staff=None type=quarter pitch=C#5 - │ └─ 0-- TimeSignature 3/4 - │ - ├─ TimePoint t=10 quarter=10 - │ │ - │ ├─ ending objects - │ │ │ - │ │ ├─ 0--10 Note id=n0 voice=1 staff=None type=quarter pitch=A4 - │ │ └─ 0--10 Note id=n1 voice=2 staff=None type=quarter pitch=C#5 - │ │ - │ └─ starting objects - │ │ - │ └─ 10--40 Note id=n2 voice=2 staff=None type=half. pitch=C#5 - │ - ├─ TimePoint t=30 quarter=10 - │ │ - │ ├─ ending objects - │ │ │ - │ │ └─ 0--30 Measure number=1 - │ │ - │ └─ starting objects - │ │ - │ └─ 30--40 Measure number=2 - │ - └─ TimePoint t=40 quarter=10 - │ - └─ ending objects - │ - ├─ 30--40 Measure number=2 - └─ 10--40 Note id=n2 voice=2 staff=None type=half. pitch=C#5 - -Let's see what our part with measures looks like in typeset form: - ->>> partitura.render(part) - -.. image:: images/score_example_1.png - :alt: Part with measures - :align: center - -Although the notes are there, the music is not typeset correctly, since the -first measure should have a duration of three quarter notes, but instead is -has a duration of four quarter notes. The problem is that the note *n2* -crosses a measure boundary, and thus should be tied. - -Splitting up notes using ties -============================= - -In musical notation notes that span measure boundaries are split up, and then -tied together. This can be done automatically using the function -:func:`~partitura.score.tie_notes`: - ->>> score.tie_notes(part) ->>> partitura.render(part) - -.. image:: images/score_example_2.png - :alt: Part with measures - :align: center - -Now the score looks correct. Displaying the contents reveals that the part -now has an extra quarter note *n2a* that starts at the measure boundary, -whereas the note *n2* is now a half note, ending at the measure boundary. - ->>> print(part.pretty()) -Part id="P0" name="My Part" - │ - ├─ TimePoint t=0 quarter=10 - │ │ - │ └─ starting objects - │ │ - │ ├─ 0--30 Measure number=1 - │ ├─ 0--10 Note id=n0 voice=1 staff=None type=quarter pitch=A4 - │ ├─ 0--10 Note id=n1 voice=2 staff=None type=quarter pitch=C#5 - │ └─ 0-- TimeSignature 3/4 - │ - ├─ TimePoint t=10 quarter=10 - │ │ - │ ├─ ending objects - │ │ │ - │ │ ├─ 0--10 Note id=n0 voice=1 staff=None type=quarter pitch=A4 - │ │ └─ 0--10 Note id=n1 voice=2 staff=None type=quarter pitch=C#5 - │ │ - │ └─ starting objects - │ │ - │ └─ 10--30 Note id=n2 voice=2 staff=None type=half tie_group=n2+n2a pitch=C#5 - │ - ├─ TimePoint t=30 quarter=10 - │ │ - │ ├─ ending objects - │ │ │ - │ │ ├─ 0--30 Measure number=1 - │ │ └─ 10--30 Note id=n2 voice=2 staff=None type=half tie_group=n2+n2a pitch=C#5 - │ │ - │ └─ starting objects - │ │ - │ ├─ 30--40 Measure number=2 - │ └─ 30--40 Note id=n2a voice=2 staff=None type=quarter tie_group=n2+n2a pitch=C#5 - │ - └─ TimePoint t=40 quarter=10 - │ - └─ ending objects - │ - ├─ 30--40 Measure number=2 - └─ 30--40 Note id=n2a voice=2 staff=None type=quarter tie_group=n2+n2a pitch=C#5 - - -Removing elements -================= - -Just like we can add elements to a part, we can also remove them, using the -:meth:`~partitura.score.Part.remove` method. The following lines remove the -measure instances that were added using the -:func:`~partitura.score.add_measures` function: - ->>> for measure in list(part.iter_all(score.Measure)): -... part.remove(measure) - -Note that we create a list of all measures in `part` before we remove them. This is necessary to avoid changing the contents of `part` while we iterate over it. - - -Importing MIDI files -==================== - -For quick access to note information from a MIDI file, use the function :func:`~partitura.midi_to_notearray`, as described in `Quick start: Reading note information from a MIDI file`_. In addition to this function, which returns a structured numpy array, partitura provides two further functions to load information from MIDI files, depending on whether the information should be treated as a performance or as a score (see ``_): - -* :func:`~partitura.load_performance_midi` -* :func:`~partitura.load_score_midi` - -The :func:`~partitura.load_performance_midi` returns a :class:`~partitura.performance.PerformedPart` instance. -The :class:`~partitura.performance.PerformedPart` instance stores notes, program change and control change messages. -The notes in :attr:`~partitura.performance.PerformedPart.notes` are dictionaries with the usual MIDI attributes "midi_pitch", "note_on", "note_off", etc. Additionally, there is a key called "sound_off" which returns note_off times adjusted by the sustain pedal. Set the on/off threshold value for the sustain_pedal MIDI cc message like so: - ->>> path_to_midifile = partitura.EXAMPLE_MIDI ->>> performedpart = partitura.load_performance_midi(path_to_midifile) ->>> performedpart.sustain_pedal_threshold=64 - -Setting the sustain pedal threshold to 128 will prevent the change of "sound_off" values by sustain pedal. -When the MIDI file does not contain any pedal information, the "sound_off" is equal to "note_off", and setting :attr:`~partitura.performance.PerformedPart.sustain_pedal_threshold` has no effect. -Calling :attr:`~partitura.performance.PerformedPart.note_array()` will return a structured array like :func:`~partitura.midi_to_notearray`. -The values in `note_array["duration_sec"]` are the actual duration of the note based on the `sound_off` time. - -The function :func:`~partitura.load_score_midi` returns a :class:`~partitura.score.Part` instance. -The function estimates the score structure based on the "parts per quarter" value and the note_on/note_off times in a MIDI file. -This function *only* works with deadpan "score" MIDI files that can be generated by Digital Audio Workstations, Scorewriters, and other sequencers. -It is not suitable to estimate the score from a performed MIDI file, such as a recording of a pianist playing on a MIDI keyboard. - ->>> midipart = partitura.load_score_midi(path_to_midifile) ->>> midipart.note_array() # doctest: +NORMALIZE_WHITESPACE - array([(0., 4., 0., 4., 0, 48, 69, 1, 'n0'), - (2., 2., 2., 2., 24, 24, 72, 2, 'n1'), - (2., 2., 2., 2., 24, 24, 76, 2, 'n2')], - dtype=[('onset_beat', '`_, as returned by the :attr:`~partitura.score.Part.note_array()` attribute. - - -Key Estimation --------------- - -Key estimation is performed by the function -:func:`~partitura.musicanalysis.estimate_key`. The function returns a string representation of the root and mode of the key: - ->>> key_name = partitura.musicanalysis.estimate_key(part.note_array()) ->>> print(key_name) -C#m - -The number of sharps/flats and the mode can be inferred from the key name using the convenience function :func:`~partitura.utils.key_name_to_fifths_mode`: - ->>> partitura.utils.key_name_to_fifths_mode(key_name) -(4, 'minor') - -Pitch Spelling --------------- - -Pitch spelling estimation is performed by the function -:func:`~partitura.musicanalysis.estimate_spelling`. The function returns a structured array with pitch spelling information (i.e., with fields `step`, `alter` and `octave`) for each note in the input `note_array`. If the input to this method is an instance of :class:`~partitura.score.Part`, :class:`~partitura.score.PartGroup`, or :class:`~partitura.performance.PerformedPart`, a list of :class:`~partitura.score.Part`, each row of the output corresponds to order of the notes in the `note_array` that would be generated by using the helper method :func:`~partitura.utils.ensure_notearray`. - ->>> pitch_spelling = partitura.musicanalysis.estimate_spelling(part.note_array()) ->>> print(pitch_spelling) -[('A', 0, 4) ('C', 1, 5) ('C', 1, 5)] - -Voice Estimation ----------------- - -Voice estimation is performed by the function -:func:`~partitura.musicanalysis.estimate_voices`. The function returns a numpy array with voice information for each note in the input `note_array`. If the input to this method is an instance of :class:`~partitura.score.Part`, :class:`~partitura.score.PartGroup`, or :class:`~partitura.performance.PerformedPart`, a list of :class:`~partitura.score.Part`, each row of the output corresponds to order of the notes in the `note_array` that would be generated by using the helper method :func:`~partitura.utils.ensure_notearray`. - ->>> voices = partitura.musicanalysis.estimate_voices(part.note_array()) ->>> print(voices) -[1 1 1] - -Tonal Tension -------------- - -Three tonal tension features proposed by Herremans and Chew (2016) are estimated by the function -:func:`~partitura.musicanalysis.estimate_tonaltension`. The function returns a strured array with fields `cloud_diameter`, `cloud_momentum`, `tensile_strain` and `onset`. In contrast to the other methods in `partitura.musicanalysis`, the tonal tension features are not computed for each note, but for specific time points, which are specified by argument `ss`, which can be a float specifying the step size, a 1D numpy array with time values, or `'onset`', which computes the tension features at each unique onset time. - ->>> import numpy as np ->>> tonal_tension = partitura.musicanalysis.estimate_tonaltension(part, ss='onset') ->>> print(np.unique(part.note_array['onset_beat'])) -[0. 1.] ->>> print(tonal_tension.dtype.names) -('onset_beat', 'cloud_diameter', 'cloud_momentum', 'tensile_strain') ->>> print(tonal_tension['cloud_momentum']) -[0. 0.16666667] - ->>> partitura.musicanalysis.estimate_spelling(part.note_array()) # doctest: +NORMALIZE_WHITESPACE -array([('A', 0, 4), ('C', 1, 5), ('C', 1, 5)], - dtype=[('step', ' Score: """ Load a score format supported by partitura. Currently the accepted formats are MusicXML, MIDI, Kern and MEI, plus all formats for which @@ -18,11 +32,8 @@ def load_score(score_fn, ensure_list=False, force_note_ids="keep"): Parameters ---------- - score_fn : str or file-like object + filename : str or file-like object Filename of the score to parse, or a file-like object - ensure_list : bool - When True, return a list independent of how many part or - group elements where created. force_note_ids : (None, bool or "keep") When True each Note in the returned Part(s) will have a newly assigned unique id attribute. Existing note id attributes in @@ -33,8 +44,8 @@ def load_score(score_fn, ensure_list=False, force_note_ids="keep"): Returns ------- - part: list or Part - A score part. If `ensure_list` the output will be a list. + scr: :class:`partitura.score.Score` + A score instance. """ part = None @@ -43,8 +54,7 @@ def load_score(score_fn, ensure_list=False, force_note_ids="keep"): # Load MusicXML try: return load_musicxml( - xml=score_fn, - ensure_list=ensure_list, + filename=filename, force_note_ids=force_note_ids, ) except Exception as e: @@ -56,22 +66,20 @@ def load_score(score_fn, ensure_list=False, force_note_ids="keep"): else: assign_note_ids = True return load_score_midi( - fn=score_fn, + filename=filename, assign_note_ids=assign_note_ids, - ensure_list=ensure_list, ) except Exception as e: exception_dictionary["MIDI"] = e # Load MEI try: - return load_mei(mei_path=score_fn) + return load_mei(filename=filename) except Exception as e: exception_dictionary["MEI"] = e # Load Kern try: return load_kern( - kern_path=score_fn, - ensure_list=ensure_list, + filename=filename, force_note_ids=force_note_ids, ) except Exception as e: @@ -79,20 +87,18 @@ def load_score(score_fn, ensure_list=False, force_note_ids="keep"): # Load MuseScore try: return load_via_musescore( - fn=score_fn, + filename=filename, force_note_ids=force_note_ids, - ensure_list=ensure_list, ) except Exception as e: exception_dictionary["MuseScore"] = e try: # Load the score information from a Matchfile - _, _, part = load_match(score_fn, create_part=True) + _, _, part = load_match( + filename=filename, + create_score=True, + ) - if ensure_list: - return [part] - else: - return part except Exception as e: exception_dictionary["matchfile"] = e if part is None: @@ -103,20 +109,46 @@ def load_score(score_fn, ensure_list=False, force_note_ids="keep"): raise NotSupportedFormatError +def load_score_as_part(filename: PathLike) -> Part: + """ + load part helper function: + Load a score format supported by partitura and + merge the result in a single part + + Parameters + ---------- + filename : str or file-like object + Filename of the score to parse, or a file-like object + + Returns + ------- + part: :class:`partitura.score.Part` + A part instance. + """ + scr = load_score(filename) + part = merge_parts(scr.parts) + return part + + +# alias +lp = load_score_as_part + + +@deprecated_alias(performance_fn="filename") def load_performance( - performance_fn, - default_bpm=120, - merge_tracks=False, - first_note_at_zero=False, - pedal_threshold=64, -): + filename: PathLike, + default_bpm: Union[float, int] = 120, + merge_tracks: bool = False, + first_note_at_zero: bool = False, + pedal_threshold: int = 64, +) -> Performance: """ Load a performance format supported by partitura. Currently the accepted formats are MIDI and matchfiles. Parameters ---------- - performance_fn: str or file-like object + filename: str or file-like object Filename of the score to parse, or a file-like object default_bpm : number, optional Tempo to use wherever the MIDI does not specify a tempo. @@ -131,8 +163,8 @@ def load_performance( Returns ------- - performed_part: :class:`partitura.performance.PerformedPart` - A PerformedPart instance. + performance: :class:`partitura.performance.Performance` + A `Performance` instance. TODO ---- @@ -140,13 +172,13 @@ def load_performance( """ from partitura.utils.music import remove_silence_from_performed_part - performed_part = None + performance = None # Catch exceptions exception_dictionary = dict() try: performance = load_performance_midi( - performance_fn, + filename=filename, default_bpm=default_bpm, merge_tracks=merge_tracks, ) @@ -162,7 +194,7 @@ def load_performance( try: performance, _ = load_match( - fn=performance_fn, + filename=filename, first_note_at_zero=first_note_at_zero, pedal_threshold=pedal_threshold, ) @@ -175,4 +207,4 @@ def load_performance( print(exception) raise NotSupportedFormatError - return performed_part + return performance diff --git a/partitura/io/exportaudio.py b/partitura/io/exportaudio.py new file mode 100644 index 00000000..61ede0ca --- /dev/null +++ b/partitura/io/exportaudio.py @@ -0,0 +1,71 @@ +""" +Synthesize Partitura object to wav using additive synthesis +""" +from typing import Union, Optional +import numpy as np +from scipy.io import wavfile + +from partitura.score import ScoreLike +from partitura.performance import PerformanceLike + +from partitura.utils.synth import synthesize, SAMPLE_RATE + +from partitura.utils.misc import PathLike + +__all__ = ["save_wav"] + + +def save_wav( + input_data: Union[ScoreLike, PerformanceLike, np.ndarray], + out: Optional[PathLike] = None, + samplerate=SAMPLE_RATE, + envelope_fun="linear", + tuning="equal_temperament", + harmonic_dist: Optional[Union[str, int]] = None, + bpm: Union[float, int] = 60, +) -> Optional[np.ndarray]: + """ + Export a score (a `Score`, `Part`, `PartGroup` or list of `Part` instances), + a performance (`Performance`, `PerformedPart` or list of `PerformedPart` instances) + as a WAV file using additive synthesis + + + Parameters + ---------- + input_data : ScoreLike, PerformanceLike or np.ndarray + A partitura object with note information. + out : PathLike or None + Path of the output Wave file. If None, the method outputs + the audio signal as an array (see `audio_signal` below). + samplerate: int + The sample rate of the audio file in Hz. The default is 44100Hz. + envelope_fun: {"linear", "exp" } + The type of envelop to apply to the individual sine waves. + tuning: {"equal_temperament", "natural"} + harmonic_dist : int, "shepard" or None (optional) + Distribution of harmonics. If an integer, it is the number + of harmonics to be considered. If "shepard", it uses Shepard tones. + Default is None (i.e., only consider the fundamental frequency) + bpm : int + The bpm to render the output (if the input is a score-like object) + + Returns + ------- + audio_signal : np.ndarray + Audio signal as a 1D array. Only returned if `out` is None. + """ + # synthesize audio signal + audio_signal = synthesize( + note_info=input_data, + samplerate=samplerate, + envelope_fun=envelope_fun, + tuning=tuning, + harmonic_dist=harmonic_dist, + bpm=bpm, + ) + + if out is not None: + # Write audio signal + wavfile.write(out, samplerate, audio_signal) + else: + return audio_signal diff --git a/partitura/io/exportmatch.py b/partitura/io/exportmatch.py index 02d1b3e8..428f3b6c 100644 --- a/partitura/io/exportmatch.py +++ b/partitura/io/exportmatch.py @@ -5,6 +5,7 @@ """ import numpy as np +from typing import List, Optional, Union, Iterable from scipy.interpolate import interp1d from partitura.io.importmatch import ( @@ -22,13 +23,17 @@ MatchFile, ) import partitura.score as score +from partitura.score import ScoreLike +from partitura.performance import PerformanceLike, PerformedPart, Performance from partitura.utils.music import midi_pitch_to_pitch_spelling, MAJOR_KEYS, MINOR_KEYS +from partitura.utils.misc import deprecated_alias, PathLike + __all__ = ["save_match"] def seconds_to_midi_ticks(t, mpq=500000, ppq=480): - return int(np.round(10**6 * ppq * t / mpq)) + return int(np.round(10 ** 6 * ppq * t / mpq)) def _fifths_mode_to_match_key_name(fifths, mode): @@ -147,7 +152,7 @@ def matchfile_from_alignment( onset, offset = spart.beat_map([n.start.t, n.start.t + n.duration_tied]) duration = offset - onset beat = (onset - bar_start) // 1 - ts_num, ts_den = spart.time_signature_map(n.start.t) + ts_num, ts_den, _ = spart.time_signature_map(n.start.t) # In metrical offset in whole notes moffset = (onset - bar_start - beat) / ts_den # offset = onset + duration @@ -306,17 +311,18 @@ def matchfile_from_alignment( return matchfile +@deprecated_alias(spart="score_data", ppart="performance_data") def save_match( - alignment, - ppart, - spart, - out, - mpq=500000, - ppq=480, - performer=None, - composer=None, - piece=None, -): + alignment: List[dict], + performance_data: PerformanceLike, + score_data: ScoreLike, + out: PathLike = None, + mpq: int = 500000, + ppq: int = 480, + performer: Optional[str] = None, + composer: Optional[str] = None, + piece: Optional[str] = None, +) -> Optional[MatchFile]: """ Save an Alignment of a PerformedPart to a Part in a match file. @@ -325,10 +331,12 @@ def save_match( alignment : list A list of dictionaries containing alignment information. See `partitura.io.importmatch.alignment_from_matchfile`. - ppart : partitura.performance.PerformedPart - An instance of `PerformedPart` containing performance information. - spart : partitura.score.Part - An instance of `Part` containing score information. + performance_data : `PerformanceLike + The performance information as a `Performance` + score_data : `ScoreLike` + The musical score. A :class:`partitura.score.Score` object, + a :class:`partitura.score.Part`, a :class:`partitura.score.PartGroup` or + a list of these. out : str Out to export the matchfile. mpq : int @@ -341,7 +349,38 @@ def save_match( Name(s) of the composer(s) of the piece represented by `Part`. piece : str or None: Name of the piece represented by `Part`. + + Returns + ------- + matchfile: MatchFile + If no output is specified using `out`, the function returns + a `MatchFile` object. Otherwise, the function returns None. """ + + # For now, we assume that we align only one Part and a PerformedPart + + if isinstance(score_data, (score.Score, Iterable)): + spart = score_data[0] + elif isinstance(score_data, score.Part): + spart = score_data + elif isinstance(score_data, score.PartGroup): + spart = score_data.children[0] + else: + raise ValueError( + "`score_data` should be a `Score`, a `Part`, a `PartGroup` or a " + f"list of `Part` objects, but is {type(score_data)}" + ) + + if isinstance(performance_data, (Performance, Iterable)): + ppart = performance_data[0] + elif isinstance(performance_data, PerformedPart): + ppart = performance_data + else: + raise ValueError( + "`performance_data` should be a `Performance`, a `PerformedPart`, or a " + f"list of `PerformedPart` objects, but is {type(score_data)}" + ) + # Get matchfile matchfile = matchfile_from_alignment( alignment=alignment, @@ -353,5 +392,9 @@ def save_match( composer=composer, piece=piece, ) - # write matchfile - matchfile.write(out) + + if out is not None: + # write matchfile + matchfile.write(out) + else: + return matchfile diff --git a/partitura/io/exportmei.py b/partitura/io/exportmei.py index df7dd4b4..9de25a7d 100644 --- a/partitura/io/exportmei.py +++ b/partitura/io/exportmei.py @@ -1,2096 +1,2096 @@ -import partitura -import partitura.score as score -from lxml import etree -from partitura.utils.generic import partition -from partitura.utils.music import estimate_symbolic_duration -from copy import copy - - -name_space = "http://www.music-encoding.org/ns/mei" - -xml_id_string = "{http://www.w3.org/XML/1998/namespace}id" - - -def extend_key(dict_of_lists, key, value): - """extend or create a list at the given key in the given dictionary - - Parameters - ---------- - dict_of_lists: dictionary - where all values are lists - key: self explanatory - value: self explanatory - - """ - - if key in dict_of_lists.keys(): - if isinstance(value, list): - dict_of_lists[key].extend(value) - else: - dict_of_lists[key].append(value) - else: - dict_of_lists[key] = value if isinstance(value, list) else [value] - - -def calc_dur_dots_split_notes_first_temp_dur(note, measure, num_to_numbase_ratio=1): - """ - Notes have to be represented as a string of elemental notes (there is no notation for arbitrary durations) - This function calculates this string (the durations of the elemental notes and their dot counts), - whether the note crosses the measure and the temporal duration of the first elemental note - - Parameters - ---------- - note: score.GenericNote - The note whose representation as a string of elemental notes is calculated - measure: score.Measure - The measure which contains note - num_to_numbase_ratio: float, optional - scales the duration of note according to whether or not it belongs to a tuplet and which one - - - Returns - ------- - dur_dots: list of int pairs - this describes the string of elemental notes that represent the note notationally - every pair in the list contains the duration and the dot count of an elemental note and - the list is ordered by duration in decreasing order - split_notes: list or None - an empty list if note crosses measure - None if it doesn't - first_temp_dur: int or None - duration of first elemental note in partitura time - """ - - if measure == "pad": - return [], None, None - - if isinstance(note, score.GraceNote): - main_note = note.main_note - # HACK: main note should actually be always not None for a proper GraceNote - if main_note != None: - dur_dots, _, _ = calc_dur_dots_split_notes_first_temp_dur( - main_note, measure - ) - dur_dots = [(2 * dur_dots[0][0], dur_dots[0][1])] - else: - dur_dots = [(8, 0)] - note.id += "_missing_main_note" - return dur_dots, None, None +# import partitura +# import partitura.score as score +# from lxml import etree +# from partitura.utils.generic import partition +# from partitura.utils.music import estimate_symbolic_duration +# from copy import copy + + +# name_space = "http://www.music-encoding.org/ns/mei" + +# xml_id_string = "{http://www.w3.org/XML/1998/namespace}id" + + +# def extend_key(dict_of_lists, key, value): +# """extend or create a list at the given key in the given dictionary + +# Parameters +# ---------- +# dict_of_lists: dictionary +# where all values are lists +# key: self explanatory +# value: self explanatory + +# """ + +# if key in dict_of_lists.keys(): +# if isinstance(value, list): +# dict_of_lists[key].extend(value) +# else: +# dict_of_lists[key].append(value) +# else: +# dict_of_lists[key] = value if isinstance(value, list) else [value] + + +# def calc_dur_dots_split_notes_first_temp_dur(note, measure, num_to_numbase_ratio=1): +# """ +# Notes have to be represented as a string of elemental notes (there is no notation for arbitrary durations) +# This function calculates this string (the durations of the elemental notes and their dot counts), +# whether the note crosses the measure and the temporal duration of the first elemental note + +# Parameters +# ---------- +# note: score.GenericNote +# The note whose representation as a string of elemental notes is calculated +# measure: score.Measure +# The measure which contains note +# num_to_numbase_ratio: float, optional +# scales the duration of note according to whether or not it belongs to a tuplet and which one + + +# Returns +# ------- +# dur_dots: list of int pairs +# this describes the string of elemental notes that represent the note notationally +# every pair in the list contains the duration and the dot count of an elemental note and +# the list is ordered by duration in decreasing order +# split_notes: list or None +# an empty list if note crosses measure +# None if it doesn't +# first_temp_dur: int or None +# duration of first elemental note in partitura time +# """ + +# if measure == "pad": +# return [], None, None + +# if isinstance(note, score.GraceNote): +# main_note = note.main_note +# # HACK: main note should actually be always not None for a proper GraceNote +# if main_note != None: +# dur_dots, _, _ = calc_dur_dots_split_notes_first_temp_dur( +# main_note, measure +# ) +# dur_dots = [(2 * dur_dots[0][0], dur_dots[0][1])] +# else: +# dur_dots = [(8, 0)] +# note.id += "_missing_main_note" +# return dur_dots, None, None - note_duration = note.duration +# note_duration = note.duration - split_notes = None +# split_notes = None - if note.start.t + note.duration > measure.end.t: - note_duration = measure.end.t - note.start.t - split_notes = [] +# if note.start.t + note.duration > measure.end.t: +# note_duration = measure.end.t - note.start.t +# split_notes = [] - quarter_dur = measure.start.quarter - fraction = num_to_numbase_ratio * note_duration / quarter_dur +# quarter_dur = measure.start.quarter +# fraction = num_to_numbase_ratio * note_duration / quarter_dur - int_part = int(fraction) - frac_part = fraction - int_part +# int_part = int(fraction) +# frac_part = fraction - int_part - # calc digits of fraction in base2 - untied_durations = [] - pow_of_2 = 1 +# # calc digits of fraction in base2 +# untied_durations = [] +# pow_of_2 = 1 - while int_part > 0: - bit = int_part % 2 - untied_durations.insert(0, bit * pow_of_2) - int_part = int_part // 2 - pow_of_2 *= 2 +# while int_part > 0: +# bit = int_part % 2 +# untied_durations.insert(0, bit * pow_of_2) +# int_part = int_part // 2 +# pow_of_2 *= 2 - pow_of_2 = 1 / 2 +# pow_of_2 = 1 / 2 - while frac_part > 0: - frac_part *= 2 - bit = int(frac_part) - frac_part -= bit - untied_durations.append(bit * pow_of_2) - pow_of_2 /= 2 +# while frac_part > 0: +# frac_part *= 2 +# bit = int(frac_part) +# frac_part -= bit +# untied_durations.append(bit * pow_of_2) +# pow_of_2 /= 2 - dur_dots = [] - - curr_dur = 0 - curr_dots = 0 - - def add_dd(dur_dots, dur, dots): - dur_dots.append((int(4 / dur), dots)) +# dur_dots = [] + +# curr_dur = 0 +# curr_dots = 0 + +# def add_dd(dur_dots, dur, dots): +# dur_dots.append((int(4 / dur), dots)) - for untied_dur in untied_durations: - if curr_dur != 0: - if untied_dur == 0: - add_dd(dur_dots, curr_dur, curr_dots) - curr_dots = 0 - curr_dur = 0 - else: - curr_dots += 1 - else: - curr_dur = untied_dur - - if curr_dur != 0: - add_dd(dur_dots, curr_dur, curr_dots) - - first_temp_dur = int(untied_durations[0] * quarter_dur) - - return dur_dots, split_notes, first_temp_dur - - -def insert_elem_check(t, inbetween_notes_elems): - """Check if something like a clef etc appears before time t - - Parameters - ---------- - t: int - time from a Timepoint - inbetween_notes_elems: list of InbetweenNotesElements - a list of objects describing things like clefs etc - - Returns - ------- - True if something like a clef etc appears before time t - """ - - for ine in inbetween_notes_elems: - if ine.elem != None and ine.elem.start.t <= t: - return True - - return False - - -def partition_handle_none(func, iter, partition_attrib): - p = partition(func, iter) - newKey = None - - if None in p.keys(): - raise KeyError( - 'PARTITION ERROR: some elements of set do not have partition attribute "' - + partition_attrib - + '"' - ) +# for untied_dur in untied_durations: +# if curr_dur != 0: +# if untied_dur == 0: +# add_dd(dur_dots, curr_dur, curr_dots) +# curr_dots = 0 +# curr_dur = 0 +# else: +# curr_dots += 1 +# else: +# curr_dur = untied_dur + +# if curr_dur != 0: +# add_dd(dur_dots, curr_dur, curr_dots) + +# first_temp_dur = int(untied_durations[0] * quarter_dur) + +# return dur_dots, split_notes, first_temp_dur + + +# def insert_elem_check(t, inbetween_notes_elems): +# """Check if something like a clef etc appears before time t + +# Parameters +# ---------- +# t: int +# time from a Timepoint +# inbetween_notes_elems: list of InbetweenNotesElements +# a list of objects describing things like clefs etc + +# Returns +# ------- +# True if something like a clef etc appears before time t +# """ + +# for ine in inbetween_notes_elems: +# if ine.elem != None and ine.elem.start.t <= t: +# return True + +# return False + + +# def partition_handle_none(func, iter, partition_attrib): +# p = partition(func, iter) +# newKey = None + +# if None in p.keys(): +# raise KeyError( +# 'PARTITION ERROR: some elements of set do not have partition attribute "' +# + partition_attrib +# + '"' +# ) - return p +# return p -def add_child(parent, child_name): - return etree.SubElement(parent, child_name) +# def add_child(parent, child_name): +# return etree.SubElement(parent, child_name) -def set_attributes(elem, *list_attrib_val): - for attrib_val in list_attrib_val: - elem.set(attrib_val[0], str(attrib_val[1])) +# def set_attributes(elem, *list_attrib_val): +# for attrib_val in list_attrib_val: +# elem.set(attrib_val[0], str(attrib_val[1])) -def attribs_of_key_sig(ks): - """ - Returns values of a score.KeySignature object necessary for a MEI document +# def attribs_of_key_sig(ks): +# """ +# Returns values of a score.KeySignature object necessary for a MEI document - Parameters - ---------- - ks: score.KeySignature +# Parameters +# ---------- +# ks: score.KeySignature - Returns - ------- - fifths: string - describes the circle of fifths - mode: string - "major" or "minor" - pname: string - pitch letter - """ +# Returns +# ------- +# fifths: string +# describes the circle of fifths +# mode: string +# "major" or "minor" +# pname: string +# pitch letter +# """ - key = ks.name - pname = key[0].lower() - mode = "major" - - if len(key) == 2: - mode = "minor" - - fifths = str(abs(ks.fifths)) +# key = ks.name +# pname = key[0].lower() +# mode = "major" + +# if len(key) == 2: +# mode = "minor" + +# fifths = str(abs(ks.fifths)) - if ks.fifths < 0: - fifths += "f" - elif ks.fifths > 0: - fifths += "s" - - return fifths, mode, pname - - -def first_instances_per_part( - cls, parts, start=score.TimePoint(0), end=score.TimePoint(1) -): - """ - Returns the first instances of a class (multiple objects with same start time are possible) in each part - - Parameters - ---------- - cls: class - parts: list of score.Part - start: score.TimePoint, optional - start of the range to search in - end: score.TimePoint, optional - end of the range to search in +# if ks.fifths < 0: +# fifths += "f" +# elif ks.fifths > 0: +# fifths += "s" + +# return fifths, mode, pname + + +# def first_instances_per_part( +# cls, parts, start=score.TimePoint(0), end=score.TimePoint(1) +# ): +# """ +# Returns the first instances of a class (multiple objects with same start time are possible) in each part + +# Parameters +# ---------- +# cls: class +# parts: list of score.Part +# start: score.TimePoint, optional +# start of the range to search in +# end: score.TimePoint, optional +# end of the range to search in - Returns - ------- - instances_per_part: list of list of instances of cls - sublists might be empty - if all sublists are empty, instances_per_part is empty - """ - if not isinstance(start, list): - start = [start] * len(parts) - elif not len(parts) == len(start): - raise ValueError( - "ERROR at first_instances_per_part: start times are given as list with different size to parts list" - ) - - if not isinstance(end, list): - end = [end] * len(parts) - elif not len(parts) == len(end): - raise ValueError( - "ERROR at first_instances_per_part: end times are given as list with different size to parts list" - ) - - for i in range(len(parts)): - if start[i] == None and end[i] != None or start[i] != None and end[i] == None: - raise ValueError( - "ERROR at first_instances_per_part: (start==None) != (end==None) (None elements in start have to be at same position as in end and vice versa)" - ) - - instances_per_part = [] - - non_empty = False - - for i, p in enumerate(parts): - s = start[i] - e = end[i] - - if s == None: - instances_per_part.append([]) - continue - - instances = list(p.iter_all(cls, s, e)) - - if len(instances) == 0: - instances_per_part.append([]) - continue - - non_empty = True - t = min(instances, key=lambda i: i.start.t).start.t - instances_per_part.append([i for i in instances if t == i.start.t]) - - if non_empty: - return instances_per_part - - return [] - - -def first_instance_per_part( - cls, parts, start=score.TimePoint(0), end=score.TimePoint(1) -): - """ - Reduce the result of first_instances_per_part, a 2D list, to a 1D list - If there are multiple first instances then program aborts with error message - - Parameters - ---------- - cls: class - parts: list of score.Part - start: score.TimePoint, optional - start of the range to search in - end: score.TimePoint, optional - end of the range to search in - - Returns - ------- - fipp: list of instances of cls - elements might be None - """ - fispp = first_instances_per_part(cls, parts, start, end) - - fipp = [] - - for i, fis in enumerate(fispp): - if len(fis) == 0: - fipp.append(None) - elif len(fis) == 1: - fipp.append(fis[0]) - else: - raise ValueError( - "Part " + parts[i].name, - "ID " + parts[i].id, - "has more than one instance of " - + str(cls) - + " at beginning t=0, but there should only be a single one", - ) - - return fipp - - -def first_instances(cls, part, start=score.TimePoint(0), end=score.TimePoint(1)): - """ - Returns the first instances of a class (multiple objects with same start time are possible) in the part - - Parameters - ---------- - cls: class - part: score.Part - start: score.TimePoint, optional - start of the range to search in - end: score.TimePoint, optional - end of the range to search in - - Returns - ------- - fis: list of instances of cls - might be empty - """ - fis = first_instances_per_part(cls, [part], start, end) - - if len(fis) == 0: - return [] - - return fis[0] - - -def first_instance(cls, part, start=score.TimePoint(0), end=score.TimePoint(1)): - """ - Reduce the result of first_instance_per_part, a 1D list, to an element - If there are multiple first instances then program aborts with error message - - Parameters - ---------- - cls: class - part: score.Part - start: score.TimePoint, optional - start of the range to search in - end: score.TimePoint, optional - end of the range to search in - - Returns - ------- - fi: instance of cls or None - """ - fi = first_instance_per_part(cls, [part], start, end) - - if len(fi) == 0: - return None - - return fi[0] - - -def common_signature(cls, sig_eql, parts, current_measures=None): - """ - Calculate whether a list of parts has a common signature (as in key or time signature) - - Parameters - ---------- - cls: score.KeySignature or score.TimeSignature - sig_eql: function - takes 2 signature objects as input and returns whether they are equivalent (in some sense) - parts: list of score.Part - current_measures: list of score.Measure, optional - current as in the measures of the parts that are played at the same time and are processed - - Returns - ------- - common_sig: instance of cls - might be None if there is no commonality between parts - """ - sigs = None - if current_measures != None: - # HACK: measures should probably not contain "pad" at this point, but an actual dummy measure with start and end times? - sigs = first_instance_per_part( - cls, - parts, - start=[cm.start if cm != "pad" else None for cm in current_measures], - end=[cm.end if cm != "pad" else None for cm in current_measures], - ) - else: - sigs = first_instance_per_part(cls, parts) - - if sigs == None or len(sigs) == 0 or None in sigs: - return None - - common_sig = sigs.pop() - - for sig in sigs: - if sig.start.t != common_sig.start.t or not sig_eql(sig, common_sig): - return None - - return common_sig - - -def vertical_slice(list_2d, index): - """ - Returns elements of the sublists at index in a 1D list - all sublists of list_2d have to have len > index - """ - vslice = [] - - for list_1d in list_2d: - vslice.append(list_1d[index]) - - return vslice - - -def time_sig_eql(ts1, ts2): - """ - equivalence function for score.TimeSignature objects - """ - return ts1.beats == ts2.beats and ts1.beat_type == ts2.beat_type - - -def key_sig_eql(ks1, ks2): - """ - equivalence function for score.KeySignature objects - """ - return ks1.name == ks2.name and ks1.fifths == ks2.fifths - - -def idx(len_obj): - return range(len(len_obj)) - - -def attribs_of_clef(clef): - """ - Returns values of a score.Clef object necessary for a MEI document - - Parameters - ---------- - clef: score.Clef - - Returns - ------- - sign: string - shape of clef (F,G, etc) - line: - which line to place clef on - """ - sign = clef.sign - - if sign == "percussion": - sign = "perc" - - if clef.octave_change != None and clef.octave_change != 0: - place = "above" - - if clef.octave_change < 0: - place = "below" - - return sign, clef.line, 1 + 7 * abs(clef.octave_change), place - - return sign, clef.line - - -def create_staff_def(staff_grp, clef): - """ - - Parameters - ---------- - staff_grp: etree.SubElement - clef: score.Clef - """ - staff_def = add_child(staff_grp, "staffDef") - - attribs = attribs_of_clef(clef) - set_attributes( - staff_def, - ("n", clef.number), - ("lines", 5), - ("clef.shape", attribs[0]), - ("clef.line", attribs[1]), - ) - if len(attribs) == 4: - set_attributes( - staff_def, ("clef.dis", attribs[2]), ("clef.dis.place", attribs[3]) - ) - - -def pad_measure(s, measure_per_staff, notes_within_measure_per_staff, auto_rest_count): - """ - Adds a fake measure ("pad") to the measures of the staff s and a score.Rest object to the notes - - Parameters - ---------- - s: int - staff number - measure_per_staff: dict of score.Measure objects - notes_within_measure_per_staff: dict of lists of score.GenericNote objects - auto_rest_count: int - a counter for all the score.Rest objects that are created automatically - - Returns - ------- - incremented auto rest counter - """ - - measure_per_staff[s] = "pad" - r = score.Rest(id="pR" + str(auto_rest_count), voice=1) - r.start = score.TimePoint(0) - r.end = r.start - - extend_key(notes_within_measure_per_staff, s, r) - return auto_rest_count + 1 - - -class InbetweenNotesElement: - """ - InbetweenNotesElements contain information on objects like clefs, keysignatures, etc - within the score and how to process them - - Parameters - ---------- - name: string - name of the element used in MEI - attrib_names: list of strings - names of the attributes of the MEI element - attrib_vals_of: function - a function that returns the attribute values of elem - container_dict: dict of lists of partitura objects - the container containing the required elements is at staff - staff: int - staff number - skip_index: int - init value for the cursor i (might skip 0) - - Attributes - ---------- - name: string - name of the element used in MEI - attrib_names: list of strings - names of the attributes of the MEI element - elem: instance of partitura object - attrib_vals_of: function - a function that returns the attribute values of elem - container: list of partitura objects - the container where elem gets its values from - i: int - cursor that keeps track of position in container - """ - - __slots__ = ["name", "attrib_names", "attrib_vals_of", "container", "i", "elem"] - - def __init__( - self, name, attrib_names, attrib_vals_of, container_dict, staff, skip_index - ): - self.name = name - self.attrib_names = attrib_names - self.attrib_vals_of = attrib_vals_of - - self.i = 0 - self.elem = None - - if staff in container_dict.keys(): - self.container = container_dict[staff] - if len(self.container) > skip_index: - self.elem = self.container[skip_index] - self.i = skip_index - else: - self.container = [] - - -def chord_rep(chords, chord_i): - return chords[chord_i][0] - - -def handle_beam(open_up, parents): - """ - Using a stack of MEI elements, opens and closes beams - - Parameters - ---------- - open_up: boolean - flag that indicates whether to open or close recent beam - parents: list of etree.SubElement - stack of MEI elements that contain the beam element - - Returns - ------- - unchanged open_up value - """ - if open_up: - parents.append(add_child(parents[-1], "beam")) - else: - parents.pop() - - return open_up - - -def is_chord_in_tuplet(chord_i, tuplet_indices): - """ - check if chord falls in the range of a tuplet - - Parameters - ---------- - chord_i: int - index of chord within chords array - tuplet_indices: list of int pairs - contains the index ranges of all the tuplets in a measure of a staff - - Returns - ------- - whether chord falls in the range of a tuplet - """ - for start, stop in tuplet_indices: - if start <= chord_i and chord_i <= stop: - return True - - return False - - -def calc_num_to_numbase_ratio(chord_i, chords, tuplet_indices): - """ - calculates how to scale a notes duration with regard to the tuplet it is in - - Parameters - ---------- - chord_i: int - index of chord within chords array - chords: list of list of score.GenericNote - array of chords (which are lists of notes) - tuplet_indices: list of int pairs - contains the index ranges of all the tuplets in a measure of a staff - - Returns - ------- - the num to numbase ratio of a tuplet (eg. 3 in 2 tuplet is 1.5) - """ - rep = chords[chord_i][0] - if not isinstance(rep, score.GraceNote) and is_chord_in_tuplet( - chord_i, tuplet_indices - ): - return ( - rep.symbolic_duration["actual_notes"] - / rep.symbolic_duration["normal_notes"] - ) - return 1 - - -def process_chord( - chord_i, - chords, - inbetween_notes_elements, - open_beam, - auto_beaming, - parents, - dur_dots, - split_notes, - first_temp_dur, - tuplet_indices, - ties, - measure, - layer, - tuplet_id_counter, - open_tuplet, - last_key_sig, - note_alterations, - notes_next_measure_per_staff, - next_dur_dots=None, -): - """ - creates , , , etc elements from chords - also creates , , etc elements if necessary for chords objects - also creates , , etc elements before chord objects from inbetween_notes_elements - - Parameters - ---------- - chord_i: int - index of chord within chords array - chords: list of list of score.GenericNote - chord array - inbetween_notes_elements: list of InbetweenNotesElements - check this to see if something like clef needs to get inserted before chord - open_beam: boolean - flag that indicates whether a beam is currently open - auto_beaming: boolean - flag that determines if automatic beams should be created or if it is kept manual - parents: list of etree.SubElement - stack of MEI elements that contain the most recent beam element - dur_dots: list of int pairs - describes how the chord actually gets notated via tied notes, each pair contains the duration of the notated note and its dot count - split_notes: list - this is either empty or None - if None, nothing is done with this - if an empty list, that means this chord crosses into the next measure and a chord is created for the next measure which is tied to this one - first_temp_dur: int - amount of ticks (as in partitura) of the first notated note - tuplet_indices: list of int pairs - the ranges of tuplets within the chords array - ties: dict - out parameter, contains pairs of IDs which need to be connected via ties - this function also adds to that - measure: score.Measure - - layer: etree.SubElement - the parent element of the elements created here - tuplet_id_counter: int - - open_tuplet: boolean - describes if a tuplet is open or not - last_key_sig: score.KeySignature - the key signature this chord should be interpeted in - note_alterations: dict - contains the alterations of staff positions (notes) that are relevant for this chord - notes_next_measure_per_staff: dict of lists of score.GenericNote - out parameter, add the result of split_notes into this - next_dur_dots: list of int pairs, optional - needed for proper beaming - - Returns - ------- - tuplet_id_counter: int - incremented if tuplet created - open_beam: boolean - eventually modified if beam opened or closed - open_tuplet: boolean - eventually modified if tuplet opened or closed - """ - - chord_notes = chords[chord_i] - rep = chord_notes[0] - - for ine in inbetween_notes_elements: - if insert_elem_check(rep.start.t, [ine]): - # note should maybe be split according to keysig or clef etc insertion time, right now only beaming is disrupted - if open_beam and auto_beaming: - open_beam = handle_beam(False, parents) - - xml_elem = add_child(parents[-1], ine.name) - attrib_vals = ine.attrib_vals_of(ine.elem) - - if ine.name == "keySig": - last_key_sig = ine.elem - - if len(ine.attrib_names) < len(attrib_vals): - raise ValueError( - "ERROR at insertion of inbetween_notes_elements: there are more attribute values than there are attribute names for xml element " - + ine.name - ) - - for nv in zip(ine.attrib_names[: len(attrib_vals)], attrib_vals): - set_attributes(xml_elem, nv) - - if ine.i + 1 >= len(ine.container): - ine.elem = None - else: - ine.i += 1 - ine.elem = ine.container[ine.i] - - if is_chord_in_tuplet(chord_i, tuplet_indices): - if not open_tuplet: - parents.append(add_child(parents[-1], "tuplet")) - num = rep.symbolic_duration["actual_notes"] - numbase = rep.symbolic_duration["normal_notes"] - set_attributes( - parents[-1], - (xml_id_string, "t" + str(tuplet_id_counter)), - ("num", num), - ("numbase", numbase), - ) - tuplet_id_counter += 1 - open_tuplet = True - elif open_tuplet: - parents.pop() - open_tuplet = False - - def set_dur_dots(elem, dur_dots): - dur, dots = dur_dots - set_attributes(elem, ("dur", dur)) - - if dots > 0: - set_attributes(elem, ("dots", dots)) - - if isinstance(rep, score.Note): - if auto_beaming: - # for now all notes are beamed, however some rules should be obeyed there, see Note Beaming and Grouping - - # check to close beam - if open_beam and ( - dur_dots[0][0] < 8 - or chord_i - 1 >= 0 - and type(rep) != type(chord_rep(chords, chord_i - 1)) - ): - open_beam = handle_beam(False, parents) - - # check to open beam (maybe again) - if not open_beam and dur_dots[0][0] >= 8: - # open beam if there are multiple "consecutive notes" which don't get interrupted by some element - if len(dur_dots) > 1 and not insert_elem_check( - rep.start.t + first_temp_dur, inbetween_notes_elements - ): - open_beam = handle_beam(True, parents) - - # open beam if there is just a single note that is not the last one in measure and next note in measure is of same type and fits in beam as well, without getting interrupted by some element - elif ( - len(dur_dots) <= 1 - and chord_i + 1 < len(chords) - and next_dur_dots[0][0] >= 8 - and type(rep) == type(chord_rep(chords, chord_i + 1)) - and not insert_elem_check( - chord_rep(chords, chord_i + 1).start.t, inbetween_notes_elements - ) - ): - open_beam = handle_beam(True, parents) - elif ( - open_beam - and chord_i > 0 - and rep.beam != chord_rep(chords, chord_i - 1).beam - ): - open_beam = handle_beam(False, parents) - - if not auto_beaming and not open_beam and rep.beam != None: - open_beam = handle_beam(True, parents) - - def conditional_gracify(elem, rep, chord_i, chords): - if isinstance(rep, score.GraceNote): - grace = "unacc" - - if rep.grace_type == "appoggiatura": - grace = "acc" - - set_attributes(elem, ("grace", grace)) - - if rep.steal_proportion != None: - set_attributes( - elem, ("grace.time", str(rep.steal_proportion * 100) + "%") - ) - - if chord_i == 0 or not isinstance( - chord_rep(chords, chord_i - 1), score.GraceNote - ): - chords[chord_i] = [copy(n) for n in chords[chord_i]] - - for n in chords[chord_i]: - n.tie_next = n.main_note - - def create_note(parent, n, id, last_key_sig, note_alterations): - note = add_child(parent, "note") - - step = n.step.lower() - set_attributes( - note, (xml_id_string, id), ("pname", step), ("oct", n.octave) - ) - - if n.articulations != None and len(n.articulations) > 0: - artics = [] - - translation = { - "accent": "acc", - "staccato": "stacc", - "tenuto": "ten", - "staccatissimo": "stacciss", - "spiccato": "spicc", - "scoop": "scoop", - "plop": "plop", - "doit": "doit", - } - - for a in n.articulations: - if a in translation.keys(): - artics.append(translation[a]) - set_attributes(note, ("artic", " ".join(artics))) - - sharps = ["f", "c", "g", "d", "a", "e", "b"] - flats = list(reversed(sharps)) - - staff_pos = step + str(n.octave) - - alter = n.alter or 0 - - def set_accid(note, acc, note_alterations, staff_pos, alter): - if ( - staff_pos in note_alterations.keys() - and alter == note_alterations[staff_pos] - ): - return - set_attributes(note, ("accid", acc)) - note_alterations[staff_pos] = alter - - # sharpen note if: is sharp, is not sharpened by key or prev alt - # flatten note if: is flat, is not flattened by key or prev alt - # neutralize note if: is neutral, is sharpened/flattened by key or prev alt - - # check if note is sharpened/flattened by prev alt or key - if ( - staff_pos in note_alterations.keys() - and note_alterations[staff_pos] != 0 - or last_key_sig.fifths > 0 - and step in sharps[: last_key_sig.fifths] - or last_key_sig.fifths < 0 - and step in flats[: -last_key_sig.fifths] - ): - if alter == 0: - set_accid(note, "n", note_alterations, staff_pos, alter) - elif alter > 0: - set_accid(note, "s", note_alterations, staff_pos, alter) - elif alter < 0: - set_accid(note, "f", note_alterations, staff_pos, alter) - - return note - - if len(chord_notes) > 1: - chord = add_child(parents[-1], "chord") - - set_dur_dots(chord, dur_dots[0]) +# Returns +# ------- +# instances_per_part: list of list of instances of cls +# sublists might be empty +# if all sublists are empty, instances_per_part is empty +# """ +# if not isinstance(start, list): +# start = [start] * len(parts) +# elif not len(parts) == len(start): +# raise ValueError( +# "ERROR at first_instances_per_part: start times are given as list with different size to parts list" +# ) + +# if not isinstance(end, list): +# end = [end] * len(parts) +# elif not len(parts) == len(end): +# raise ValueError( +# "ERROR at first_instances_per_part: end times are given as list with different size to parts list" +# ) + +# for i in range(len(parts)): +# if start[i] == None and end[i] != None or start[i] != None and end[i] == None: +# raise ValueError( +# "ERROR at first_instances_per_part: (start==None) != (end==None) (None elements in start have to be at same position as in end and vice versa)" +# ) + +# instances_per_part = [] + +# non_empty = False + +# for i, p in enumerate(parts): +# s = start[i] +# e = end[i] + +# if s == None: +# instances_per_part.append([]) +# continue + +# instances = list(p.iter_all(cls, s, e)) + +# if len(instances) == 0: +# instances_per_part.append([]) +# continue + +# non_empty = True +# t = min(instances, key=lambda i: i.start.t).start.t +# instances_per_part.append([i for i in instances if t == i.start.t]) + +# if non_empty: +# return instances_per_part + +# return [] + + +# def first_instance_per_part( +# cls, parts, start=score.TimePoint(0), end=score.TimePoint(1) +# ): +# """ +# Reduce the result of first_instances_per_part, a 2D list, to a 1D list +# If there are multiple first instances then program aborts with error message + +# Parameters +# ---------- +# cls: class +# parts: list of score.Part +# start: score.TimePoint, optional +# start of the range to search in +# end: score.TimePoint, optional +# end of the range to search in + +# Returns +# ------- +# fipp: list of instances of cls +# elements might be None +# """ +# fispp = first_instances_per_part(cls, parts, start, end) + +# fipp = [] + +# for i, fis in enumerate(fispp): +# if len(fis) == 0: +# fipp.append(None) +# elif len(fis) == 1: +# fipp.append(fis[0]) +# else: +# raise ValueError( +# "Part " + parts[i].name, +# "ID " + parts[i].id, +# "has more than one instance of " +# + str(cls) +# + " at beginning t=0, but there should only be a single one", +# ) + +# return fipp + + +# def first_instances(cls, part, start=score.TimePoint(0), end=score.TimePoint(1)): +# """ +# Returns the first instances of a class (multiple objects with same start time are possible) in the part + +# Parameters +# ---------- +# cls: class +# part: score.Part +# start: score.TimePoint, optional +# start of the range to search in +# end: score.TimePoint, optional +# end of the range to search in + +# Returns +# ------- +# fis: list of instances of cls +# might be empty +# """ +# fis = first_instances_per_part(cls, [part], start, end) + +# if len(fis) == 0: +# return [] + +# return fis[0] + + +# def first_instance(cls, part, start=score.TimePoint(0), end=score.TimePoint(1)): +# """ +# Reduce the result of first_instance_per_part, a 1D list, to an element +# If there are multiple first instances then program aborts with error message + +# Parameters +# ---------- +# cls: class +# part: score.Part +# start: score.TimePoint, optional +# start of the range to search in +# end: score.TimePoint, optional +# end of the range to search in + +# Returns +# ------- +# fi: instance of cls or None +# """ +# fi = first_instance_per_part(cls, [part], start, end) + +# if len(fi) == 0: +# return None + +# return fi[0] + + +# def common_signature(cls, sig_eql, parts, current_measures=None): +# """ +# Calculate whether a list of parts has a common signature (as in key or time signature) + +# Parameters +# ---------- +# cls: score.KeySignature or score.TimeSignature +# sig_eql: function +# takes 2 signature objects as input and returns whether they are equivalent (in some sense) +# parts: list of score.Part +# current_measures: list of score.Measure, optional +# current as in the measures of the parts that are played at the same time and are processed + +# Returns +# ------- +# common_sig: instance of cls +# might be None if there is no commonality between parts +# """ +# sigs = None +# if current_measures != None: +# # HACK: measures should probably not contain "pad" at this point, but an actual dummy measure with start and end times? +# sigs = first_instance_per_part( +# cls, +# parts, +# start=[cm.start if cm != "pad" else None for cm in current_measures], +# end=[cm.end if cm != "pad" else None for cm in current_measures], +# ) +# else: +# sigs = first_instance_per_part(cls, parts) + +# if sigs == None or len(sigs) == 0 or None in sigs: +# return None + +# common_sig = sigs.pop() + +# for sig in sigs: +# if sig.start.t != common_sig.start.t or not sig_eql(sig, common_sig): +# return None + +# return common_sig + + +# def vertical_slice(list_2d, index): +# """ +# Returns elements of the sublists at index in a 1D list +# all sublists of list_2d have to have len > index +# """ +# vslice = [] + +# for list_1d in list_2d: +# vslice.append(list_1d[index]) + +# return vslice + + +# def time_sig_eql(ts1, ts2): +# """ +# equivalence function for score.TimeSignature objects +# """ +# return ts1.beats == ts2.beats and ts1.beat_type == ts2.beat_type + + +# def key_sig_eql(ks1, ks2): +# """ +# equivalence function for score.KeySignature objects +# """ +# return ks1.name == ks2.name and ks1.fifths == ks2.fifths + + +# def idx(len_obj): +# return range(len(len_obj)) + + +# def attribs_of_clef(clef): +# """ +# Returns values of a score.Clef object necessary for a MEI document + +# Parameters +# ---------- +# clef: score.Clef + +# Returns +# ------- +# sign: string +# shape of clef (F,G, etc) +# line: +# which line to place clef on +# """ +# sign = clef.sign + +# if sign == "percussion": +# sign = "perc" + +# if clef.octave_change != None and clef.octave_change != 0: +# place = "above" + +# if clef.octave_change < 0: +# place = "below" + +# return sign, clef.line, 1 + 7 * abs(clef.octave_change), place + +# return sign, clef.line + + +# def create_staff_def(staff_grp, clef): +# """ + +# Parameters +# ---------- +# staff_grp: etree.SubElement +# clef: score.Clef +# """ +# staff_def = add_child(staff_grp, "staffDef") + +# attribs = attribs_of_clef(clef) +# set_attributes( +# staff_def, +# ("n", clef.number), +# ("lines", 5), +# ("clef.shape", attribs[0]), +# ("clef.line", attribs[1]), +# ) +# if len(attribs) == 4: +# set_attributes( +# staff_def, ("clef.dis", attribs[2]), ("clef.dis.place", attribs[3]) +# ) + + +# def pad_measure(s, measure_per_staff, notes_within_measure_per_staff, auto_rest_count): +# """ +# Adds a fake measure ("pad") to the measures of the staff s and a score.Rest object to the notes + +# Parameters +# ---------- +# s: int +# staff number +# measure_per_staff: dict of score.Measure objects +# notes_within_measure_per_staff: dict of lists of score.GenericNote objects +# auto_rest_count: int +# a counter for all the score.Rest objects that are created automatically + +# Returns +# ------- +# incremented auto rest counter +# """ + +# measure_per_staff[s] = "pad" +# r = score.Rest(id="pR" + str(auto_rest_count), voice=1) +# r.start = score.TimePoint(0) +# r.end = r.start + +# extend_key(notes_within_measure_per_staff, s, r) +# return auto_rest_count + 1 + + +# class InbetweenNotesElement: +# """ +# InbetweenNotesElements contain information on objects like clefs, keysignatures, etc +# within the score and how to process them + +# Parameters +# ---------- +# name: string +# name of the element used in MEI +# attrib_names: list of strings +# names of the attributes of the MEI element +# attrib_vals_of: function +# a function that returns the attribute values of elem +# container_dict: dict of lists of partitura objects +# the container containing the required elements is at staff +# staff: int +# staff number +# skip_index: int +# init value for the cursor i (might skip 0) + +# Attributes +# ---------- +# name: string +# name of the element used in MEI +# attrib_names: list of strings +# names of the attributes of the MEI element +# elem: instance of partitura object +# attrib_vals_of: function +# a function that returns the attribute values of elem +# container: list of partitura objects +# the container where elem gets its values from +# i: int +# cursor that keeps track of position in container +# """ + +# __slots__ = ["name", "attrib_names", "attrib_vals_of", "container", "i", "elem"] + +# def __init__( +# self, name, attrib_names, attrib_vals_of, container_dict, staff, skip_index +# ): +# self.name = name +# self.attrib_names = attrib_names +# self.attrib_vals_of = attrib_vals_of + +# self.i = 0 +# self.elem = None + +# if staff in container_dict.keys(): +# self.container = container_dict[staff] +# if len(self.container) > skip_index: +# self.elem = self.container[skip_index] +# self.i = skip_index +# else: +# self.container = [] + + +# def chord_rep(chords, chord_i): +# return chords[chord_i][0] + + +# def handle_beam(open_up, parents): +# """ +# Using a stack of MEI elements, opens and closes beams + +# Parameters +# ---------- +# open_up: boolean +# flag that indicates whether to open or close recent beam +# parents: list of etree.SubElement +# stack of MEI elements that contain the beam element + +# Returns +# ------- +# unchanged open_up value +# """ +# if open_up: +# parents.append(add_child(parents[-1], "beam")) +# else: +# parents.pop() + +# return open_up + + +# def is_chord_in_tuplet(chord_i, tuplet_indices): +# """ +# check if chord falls in the range of a tuplet + +# Parameters +# ---------- +# chord_i: int +# index of chord within chords array +# tuplet_indices: list of int pairs +# contains the index ranges of all the tuplets in a measure of a staff + +# Returns +# ------- +# whether chord falls in the range of a tuplet +# """ +# for start, stop in tuplet_indices: +# if start <= chord_i and chord_i <= stop: +# return True + +# return False + + +# def calc_num_to_numbase_ratio(chord_i, chords, tuplet_indices): +# """ +# calculates how to scale a notes duration with regard to the tuplet it is in + +# Parameters +# ---------- +# chord_i: int +# index of chord within chords array +# chords: list of list of score.GenericNote +# array of chords (which are lists of notes) +# tuplet_indices: list of int pairs +# contains the index ranges of all the tuplets in a measure of a staff + +# Returns +# ------- +# the num to numbase ratio of a tuplet (eg. 3 in 2 tuplet is 1.5) +# """ +# rep = chords[chord_i][0] +# if not isinstance(rep, score.GraceNote) and is_chord_in_tuplet( +# chord_i, tuplet_indices +# ): +# return ( +# rep.symbolic_duration["actual_notes"] +# / rep.symbolic_duration["normal_notes"] +# ) +# return 1 + + +# def process_chord( +# chord_i, +# chords, +# inbetween_notes_elements, +# open_beam, +# auto_beaming, +# parents, +# dur_dots, +# split_notes, +# first_temp_dur, +# tuplet_indices, +# ties, +# measure, +# layer, +# tuplet_id_counter, +# open_tuplet, +# last_key_sig, +# note_alterations, +# notes_next_measure_per_staff, +# next_dur_dots=None, +# ): +# """ +# creates , , , etc elements from chords +# also creates , , etc elements if necessary for chords objects +# also creates , , etc elements before chord objects from inbetween_notes_elements + +# Parameters +# ---------- +# chord_i: int +# index of chord within chords array +# chords: list of list of score.GenericNote +# chord array +# inbetween_notes_elements: list of InbetweenNotesElements +# check this to see if something like clef needs to get inserted before chord +# open_beam: boolean +# flag that indicates whether a beam is currently open +# auto_beaming: boolean +# flag that determines if automatic beams should be created or if it is kept manual +# parents: list of etree.SubElement +# stack of MEI elements that contain the most recent beam element +# dur_dots: list of int pairs +# describes how the chord actually gets notated via tied notes, each pair contains the duration of the notated note and its dot count +# split_notes: list +# this is either empty or None +# if None, nothing is done with this +# if an empty list, that means this chord crosses into the next measure and a chord is created for the next measure which is tied to this one +# first_temp_dur: int +# amount of ticks (as in partitura) of the first notated note +# tuplet_indices: list of int pairs +# the ranges of tuplets within the chords array +# ties: dict +# out parameter, contains pairs of IDs which need to be connected via ties +# this function also adds to that +# measure: score.Measure + +# layer: etree.SubElement +# the parent element of the elements created here +# tuplet_id_counter: int + +# open_tuplet: boolean +# describes if a tuplet is open or not +# last_key_sig: score.KeySignature +# the key signature this chord should be interpeted in +# note_alterations: dict +# contains the alterations of staff positions (notes) that are relevant for this chord +# notes_next_measure_per_staff: dict of lists of score.GenericNote +# out parameter, add the result of split_notes into this +# next_dur_dots: list of int pairs, optional +# needed for proper beaming + +# Returns +# ------- +# tuplet_id_counter: int +# incremented if tuplet created +# open_beam: boolean +# eventually modified if beam opened or closed +# open_tuplet: boolean +# eventually modified if tuplet opened or closed +# """ + +# chord_notes = chords[chord_i] +# rep = chord_notes[0] + +# for ine in inbetween_notes_elements: +# if insert_elem_check(rep.start.t, [ine]): +# # note should maybe be split according to keysig or clef etc insertion time, right now only beaming is disrupted +# if open_beam and auto_beaming: +# open_beam = handle_beam(False, parents) + +# xml_elem = add_child(parents[-1], ine.name) +# attrib_vals = ine.attrib_vals_of(ine.elem) + +# if ine.name == "keySig": +# last_key_sig = ine.elem + +# if len(ine.attrib_names) < len(attrib_vals): +# raise ValueError( +# "ERROR at insertion of inbetween_notes_elements: there are more attribute values than there are attribute names for xml element " +# + ine.name +# ) + +# for nv in zip(ine.attrib_names[: len(attrib_vals)], attrib_vals): +# set_attributes(xml_elem, nv) + +# if ine.i + 1 >= len(ine.container): +# ine.elem = None +# else: +# ine.i += 1 +# ine.elem = ine.container[ine.i] + +# if is_chord_in_tuplet(chord_i, tuplet_indices): +# if not open_tuplet: +# parents.append(add_child(parents[-1], "tuplet")) +# num = rep.symbolic_duration["actual_notes"] +# numbase = rep.symbolic_duration["normal_notes"] +# set_attributes( +# parents[-1], +# (xml_id_string, "t" + str(tuplet_id_counter)), +# ("num", num), +# ("numbase", numbase), +# ) +# tuplet_id_counter += 1 +# open_tuplet = True +# elif open_tuplet: +# parents.pop() +# open_tuplet = False + +# def set_dur_dots(elem, dur_dots): +# dur, dots = dur_dots +# set_attributes(elem, ("dur", dur)) + +# if dots > 0: +# set_attributes(elem, ("dots", dots)) + +# if isinstance(rep, score.Note): +# if auto_beaming: +# # for now all notes are beamed, however some rules should be obeyed there, see Note Beaming and Grouping + +# # check to close beam +# if open_beam and ( +# dur_dots[0][0] < 8 +# or chord_i - 1 >= 0 +# and type(rep) != type(chord_rep(chords, chord_i - 1)) +# ): +# open_beam = handle_beam(False, parents) + +# # check to open beam (maybe again) +# if not open_beam and dur_dots[0][0] >= 8: +# # open beam if there are multiple "consecutive notes" which don't get interrupted by some element +# if len(dur_dots) > 1 and not insert_elem_check( +# rep.start.t + first_temp_dur, inbetween_notes_elements +# ): +# open_beam = handle_beam(True, parents) + +# # open beam if there is just a single note that is not the last one in measure and next note in measure is of same type and fits in beam as well, without getting interrupted by some element +# elif ( +# len(dur_dots) <= 1 +# and chord_i + 1 < len(chords) +# and next_dur_dots[0][0] >= 8 +# and type(rep) == type(chord_rep(chords, chord_i + 1)) +# and not insert_elem_check( +# chord_rep(chords, chord_i + 1).start.t, inbetween_notes_elements +# ) +# ): +# open_beam = handle_beam(True, parents) +# elif ( +# open_beam +# and chord_i > 0 +# and rep.beam != chord_rep(chords, chord_i - 1).beam +# ): +# open_beam = handle_beam(False, parents) + +# if not auto_beaming and not open_beam and rep.beam != None: +# open_beam = handle_beam(True, parents) + +# def conditional_gracify(elem, rep, chord_i, chords): +# if isinstance(rep, score.GraceNote): +# grace = "unacc" + +# if rep.grace_type == "appoggiatura": +# grace = "acc" + +# set_attributes(elem, ("grace", grace)) + +# if rep.steal_proportion != None: +# set_attributes( +# elem, ("grace.time", str(rep.steal_proportion * 100) + "%") +# ) + +# if chord_i == 0 or not isinstance( +# chord_rep(chords, chord_i - 1), score.GraceNote +# ): +# chords[chord_i] = [copy(n) for n in chords[chord_i]] + +# for n in chords[chord_i]: +# n.tie_next = n.main_note + +# def create_note(parent, n, id, last_key_sig, note_alterations): +# note = add_child(parent, "note") + +# step = n.step.lower() +# set_attributes( +# note, (xml_id_string, id), ("pname", step), ("oct", n.octave) +# ) + +# if n.articulations != None and len(n.articulations) > 0: +# artics = [] + +# translation = { +# "accent": "acc", +# "staccato": "stacc", +# "tenuto": "ten", +# "staccatissimo": "stacciss", +# "spiccato": "spicc", +# "scoop": "scoop", +# "plop": "plop", +# "doit": "doit", +# } + +# for a in n.articulations: +# if a in translation.keys(): +# artics.append(translation[a]) +# set_attributes(note, ("artic", " ".join(artics))) + +# sharps = ["f", "c", "g", "d", "a", "e", "b"] +# flats = list(reversed(sharps)) + +# staff_pos = step + str(n.octave) + +# alter = n.alter or 0 + +# def set_accid(note, acc, note_alterations, staff_pos, alter): +# if ( +# staff_pos in note_alterations.keys() +# and alter == note_alterations[staff_pos] +# ): +# return +# set_attributes(note, ("accid", acc)) +# note_alterations[staff_pos] = alter + +# # sharpen note if: is sharp, is not sharpened by key or prev alt +# # flatten note if: is flat, is not flattened by key or prev alt +# # neutralize note if: is neutral, is sharpened/flattened by key or prev alt + +# # check if note is sharpened/flattened by prev alt or key +# if ( +# staff_pos in note_alterations.keys() +# and note_alterations[staff_pos] != 0 +# or last_key_sig.fifths > 0 +# and step in sharps[: last_key_sig.fifths] +# or last_key_sig.fifths < 0 +# and step in flats[: -last_key_sig.fifths] +# ): +# if alter == 0: +# set_accid(note, "n", note_alterations, staff_pos, alter) +# elif alter > 0: +# set_accid(note, "s", note_alterations, staff_pos, alter) +# elif alter < 0: +# set_accid(note, "f", note_alterations, staff_pos, alter) + +# return note + +# if len(chord_notes) > 1: +# chord = add_child(parents[-1], "chord") + +# set_dur_dots(chord, dur_dots[0]) - conditional_gracify(chord, rep, chord_i, chords) +# conditional_gracify(chord, rep, chord_i, chords) - for n in chord_notes: - create_note(chord, n, n.id, last_key_sig, note_alterations) +# for n in chord_notes: +# create_note(chord, n, n.id, last_key_sig, note_alterations) - else: - note = create_note(parents[-1], rep, rep.id, last_key_sig, note_alterations) - set_dur_dots(note, dur_dots[0]) +# else: +# note = create_note(parents[-1], rep, rep.id, last_key_sig, note_alterations) +# set_dur_dots(note, dur_dots[0]) - conditional_gracify(note, rep, chord_i, chords) +# conditional_gracify(note, rep, chord_i, chords) - if len(dur_dots) > 1: - for n in chord_notes: - ties[n.id] = [n.id] +# if len(dur_dots) > 1: +# for n in chord_notes: +# ties[n.id] = [n.id] - def create_split_up_notes(chord_notes, i, parents, dur_dots, ties, rep): - if len(chord_notes) > 1: - chord = add_child(parents[-1], "chord") - set_dur_dots(chord, dur_dots[i]) - - for n in chord_notes: - id = n.id + "-" + str(i) - - ties[n.id].append(id) - create_note(chord, n, id, last_key_sig, note_alterations) - else: - id = rep.id + "-" + str(i) - - ties[rep.id].append(id) +# def create_split_up_notes(chord_notes, i, parents, dur_dots, ties, rep): +# if len(chord_notes) > 1: +# chord = add_child(parents[-1], "chord") +# set_dur_dots(chord, dur_dots[i]) + +# for n in chord_notes: +# id = n.id + "-" + str(i) + +# ties[n.id].append(id) +# create_note(chord, n, id, last_key_sig, note_alterations) +# else: +# id = rep.id + "-" + str(i) + +# ties[rep.id].append(id) - note = create_note( - parents[-1], rep, id, last_key_sig, note_alterations - ) - - set_dur_dots(note, dur_dots[i]) - - for i in range(1, len(dur_dots) - 1): - if not open_beam and dur_dots[i][0] >= 8: - open_beam = handle_beam(True, parents) - - create_split_up_notes(chord_notes, i, parents, dur_dots, ties, rep) - - create_split_up_notes( - chord_notes, len(dur_dots) - 1, parents, dur_dots, ties, rep - ) +# note = create_note( +# parents[-1], rep, id, last_key_sig, note_alterations +# ) + +# set_dur_dots(note, dur_dots[i]) + +# for i in range(1, len(dur_dots) - 1): +# if not open_beam and dur_dots[i][0] >= 8: +# open_beam = handle_beam(True, parents) + +# create_split_up_notes(chord_notes, i, parents, dur_dots, ties, rep) + +# create_split_up_notes( +# chord_notes, len(dur_dots) - 1, parents, dur_dots, ties, rep +# ) - if split_notes != None: +# if split_notes != None: - for n in chord_notes: - split_notes.append(score.Note(n.step, n.octave, id=n.id + "s")) +# for n in chord_notes: +# split_notes.append(score.Note(n.step, n.octave, id=n.id + "s")) - if len(dur_dots) > 1: - for n in chord_notes: - ties[n.id].append(n.id + "s") - else: - for n in chord_notes: - ties[n.id] = [n.id, n.id + "s"] +# if len(dur_dots) > 1: +# for n in chord_notes: +# ties[n.id].append(n.id + "s") +# else: +# for n in chord_notes: +# ties[n.id] = [n.id, n.id + "s"] - for n in chord_notes: - if n.tie_next != None: - if n.id in ties.keys(): - ties[n.id].append(n.tie_next.id) - else: - ties[n.id] = [n.id, n.tie_next.id] - - elif isinstance(rep, score.Rest): - if split_notes != None: - split_notes.append(score.Rest(id=rep.id + "s")) - - if ( - measure == "pad" - or measure.start.t == rep.start.t - and measure.end.t == rep.end.t - ): - rest = add_child(layer, "mRest") - - set_attributes(rest, (xml_id_string, rep.id)) - else: - rest = add_child(layer, "rest") - - set_attributes(rest, (xml_id_string, rep.id)) - - set_dur_dots(rest, dur_dots[0]) - - for i in range(1, len(dur_dots)): - rest = add_child(layer, "rest") - - id = rep.id + str(i) - - set_attributes(rest, (xml_id_string, id)) - set_dur_dots(rest, dur_dots[i]) - - if split_notes != None: - for sn in split_notes: - sn.voice = rep.voice - sn.start = measure.end - sn.end = score.TimePoint(rep.start.t + rep.duration) - - extend_key(notes_next_measure_per_staff, s, sn) - - return tuplet_id_counter, open_beam, open_tuplet - - -def create_score_def(measures, measure_i, parts, parent): - """ - creates - - Parameters - ---------- - measures: list of score.Measure - measure_i: int - index of measure currently processed within measures - parts: list of score.Part - parent: etree.SubElement - parent of - """ - reference_measures = vertical_slice(measures, measure_i) - - common_key_sig = common_signature( - score.KeySignature, key_sig_eql, parts, reference_measures - ) - common_time_sig = common_signature( - score.TimeSignature, time_sig_eql, parts, reference_measures - ) - - score_def = None - - if common_key_sig != None or common_time_sig != None: - score_def = add_child(parent, "scoreDef") - - if common_key_sig != None: - fifths, mode, pname = attribs_of_key_sig(common_key_sig) - - set_attributes( - score_def, ("key.sig", fifths), ("key.mode", mode), ("key.pname", pname) - ) - - if common_time_sig != None: - set_attributes( - score_def, - ("meter.count", common_time_sig.beats), - ("meter.unit", common_time_sig.beat_type), - ) - - return score_def - - -class MeasureContent: - """ - Simply a bundle for all the data of a measure that needs to be processed for a MEI document - - Attributes - ---------- - ties_per_staff: dict of lists - clefs_per_staff: dict of lists - key_sigs_per_staff: dict of lists - time_sigs_per_staff: dict of lists - measure_per_staff: dict of lists - tuplets_per_staff: dict of lists - slurs: list - dirs: list - dynams: list - tempii: list - fermatas: list - """ - - __slots__ = [ - "ties_per_staff", - "clefs_per_staff", - "key_sigs_per_staff", - "time_sigs_per_staff", - "measure_per_staff", - "tuplets_per_staff", - "slurs", - "dirs", - "dynams", - "tempii", - "fermatas", - ] - - def __init__(self): - self.ties_per_staff = {} - self.clefs_per_staff = {} - self.key_sigs_per_staff = {} - self.time_sigs_per_staff = {} - self.measure_per_staff = {} - self.tuplets_per_staff = {} - - self.slurs = [] - self.dirs = [] - self.dynams = [] - self.tempii = [] - self.fermatas = [] - - -def extract_from_measures( - parts, - measures, - measure_i, - staves_per_part, - auto_rest_count, - notes_within_measure_per_staff, -): - """ - Returns a bundle of data regarding the measure currently processed, things like notes, key signatures, etc - Also creates padding measures, necessary for example, for staves of instruments which do not play in the current measure - - Parameters - ---------- - parts: list of score.Part - measures: list of score.Measure - measure_i: int - index of current measure within measures - staves_per_part: dict of list of ints - staff enumeration partitioned by part - auto_rest_count: int - counter for the IDs of automatically generated rests - notes_within_measure_per_staff: dict of lists of score.GenericNote - in and out parameter, might contain note objects that have crossed from previous measure into current one - - Returns - ------- - auto_rest_count: int - incremented if score.Rest created - current_measure_content: MeasureContent - bundle for all the data that is extracted from the currently processed measure - """ - current_measure_content = MeasureContent() - - for part_i, part in enumerate(parts): - m = measures[part_i][measure_i] - - if m == "pad": - for s in staves_per_part[part_i]: - auto_rest_count = pad_measure( - s, - current_measure_content.measure_per_staff, - notes_within_measure_per_staff, - auto_rest_count, - ) - - continue - - def cls_within_measure(part, cls, measure, incl_subcls=False): - return part.iter_all( - cls, measure.start, measure.end, include_subclasses=incl_subcls - ) - - def cls_within_measure_list(part, cls, measure, incl_subcls=False): - return list(cls_within_measure(part, cls, measure, incl_subcls)) - - clefs_within_measure_per_staff_per_part = partition_handle_none( - lambda c: c.number, cls_within_measure(part, score.Clef, m), "number" - ) - key_sigs_within_measure = cls_within_measure_list(part, score.KeySignature, m) - time_sigs_within_measure = cls_within_measure_list(part, score.TimeSignature, m) - current_measure_content.slurs.extend(cls_within_measure(part, score.Slur, m)) - tuplets_within_measure = cls_within_measure_list(part, score.Tuplet, m) - - beat_map = part.beat_map - - def calc_tstamp(beat_map, t, measure): - return beat_map(t) - beat_map(measure.start.t) + 1 - - for w in cls_within_measure(part, score.Words, m): - tstamp = calc_tstamp(beat_map, w.start.t, m) - current_measure_content.dirs.append((tstamp, w)) - - for tempo in cls_within_measure(part, score.Tempo, m): - tstamp = calc_tstamp(beat_map, tempo.start.t, m) - current_measure_content.tempii.append( - (tstamp, staves_per_part[part_i][0], tempo) - ) - - for fermata in cls_within_measure(part, score.Fermata, m): - tstamp = calc_tstamp(beat_map, fermata.start.t, m) - current_measure_content.fermatas.append((tstamp, fermata.ref.staff)) - - for dynam in cls_within_measure(part, score.Direction, m, True): - tstamp = calc_tstamp(beat_map, dynam.start.t, m) - tstamp2 = None - - if dynam.end != None: - measure_counter = measure_i - while True: - if dynam.end.t <= measures[part_i][measure_counter].end.t: - tstamp2 = calc_tstamp( - beat_map, dynam.end.t, measures[part_i][measure_counter] - ) - - tstamp2 = str(measure_counter - measure_i) + "m+" + str(tstamp2) - - break - elif ( - measure_counter + 1 >= len(measures[part_i]) - or measures[part_i][measure_counter + 1] == "pad" - ): - raise ValueError( - "A score.Direction instance has an end time that exceeds actual non-padded measures" - ) - else: - measure_counter += 1 - - current_measure_content.dynams.append((tstamp, tstamp2, dynam)) - - notes_within_measure_per_staff_per_part = partition_handle_none( - lambda n: n.staff, - cls_within_measure(part, score.GenericNote, m, True), - "staff", - ) - - for s in staves_per_part[part_i]: - current_measure_content.key_sigs_per_staff[s] = key_sigs_within_measure - current_measure_content.time_sigs_per_staff[s] = time_sigs_within_measure - current_measure_content.tuplets_per_staff[s] = tuplets_within_measure - - if s not in notes_within_measure_per_staff_per_part.keys(): - auto_rest_count = pad_measure( - s, - current_measure_content.measure_per_staff, - notes_within_measure_per_staff, - auto_rest_count, - ) - - for s, nwp in notes_within_measure_per_staff_per_part.items(): - extend_key(notes_within_measure_per_staff, s, nwp) - current_measure_content.measure_per_staff[s] = m - - for s, cwp in clefs_within_measure_per_staff_per_part.items(): - current_measure_content.clefs_per_staff[s] = cwp - - return auto_rest_count, current_measure_content - - -def create_measure( - section, - measure_i, - staves_sorted, - notes_within_measure_per_staff, - score_def, - tuplet_id_counter, - auto_beaming, - last_key_sig_per_staff, - current_measure_content, -): - """ - creates a element within
- also returns an updated id counter for tuplets and a dictionary of notes that cross into the next measure - - Parameters - ---------- - section: etree.SubElement - measure_i: int - index of the measure created - staves_sorted: list of ints - a sorted list of the proper staff enumeration of the score - notes_within_measure_per_staff: dict of lists of score.GenericNote - contains score.Note, score.Rest, etc objects of the current measure, partitioned by staff enumeration - will be further partitioned and sorted by voice, time and type (score.GraceNote) and eventually gathered into - a list of equivalence classes called chords - score_def: etree.SubElement - tuplet_id_counter: int - tuplets usually don't come with IDs, so an automatic counter takes care of that - auto_beaming: boolean - enables automatic beaming - last_key_sig_per_staff: dict of score.KeySignature - keeps track of the keysignature each staff is currently in - current_measure_content: MeasureContent - contains all sorts of data for the measure like tuplets, slurs, etc - - Returns - ------- - tuplet_id_counter: int - incremented if tuplet created - notes_next_measure_per_staff: dict of lists of score.GenericNote - score.GenericNote objects that cross into the next measure - """ - measure = add_child(section, "measure") - set_attributes(measure, ("n", measure_i + 1)) - - ties_per_staff = {} - - for s in staves_sorted: - note_alterations = {} - - staff = add_child(measure, "staff") - - set_attributes(staff, ("n", s)) - - notes_within_measure_per_staff_per_voice = partition_handle_none( - lambda n: n.voice, notes_within_measure_per_staff[s], "voice" - ) - - ties_per_staff_per_voice = {} - - m = current_measure_content.measure_per_staff[s] - - tuplets = [] - if s in current_measure_content.tuplets_per_staff.keys(): - tuplets = current_measure_content.tuplets_per_staff[s] - - last_key_sig = last_key_sig_per_staff[s] - - for voice, notes in notes_within_measure_per_staff_per_voice.items(): - layer = add_child(staff, "layer") - - set_attributes(layer, ("n", voice)) - - ties = {} - - notes_partition = partition_handle_none( - lambda n: n.start.t, notes, "start.t" - ) - - chords = [] - - for t in sorted(notes_partition.keys()): - ns = notes_partition[t] - - if len(ns) > 1: - type_partition = partition_handle_none( - lambda n: isinstance(n, score.GraceNote), ns, "isGraceNote" - ) - - if True in type_partition.keys(): - gns = type_partition[True] - - gn_chords = [] - - def scan_backwards(gns): - start = gns[0] - - while isinstance(start.grace_prev, score.GraceNote): - start = start.grace_prev - - return start - - start = scan_backwards(gns) - - def process_grace_note(n, gns): - if not n in gns: - raise ValueError( - "Error at forward scan of GraceNotes: a grace_next has either different staff, voice or starting time than GraceNote chain" - ) - gns.remove(n) - return n.grace_next - - while isinstance(start, score.GraceNote): - gn_chords.append([start]) - start = process_grace_note(start, gns) - - while len(gns) > 0: - start = scan_backwards(gns) - - i = 0 - while isinstance(start, score.GraceNote): - if i >= len(gn_chords): - raise IndexError( - "ERROR at GraceNote-forward scanning: Difference in lengths of grace note sequences for different chord notes" - ) - gn_chords[i].append(start) - start = process_grace_note(start, gns) - i += 1 - - if not i == len(gn_chords): - raise IndexError( - "ERROR at GraceNote-forward scanning: Difference in lengths of grace note sequences for different chord notes" - ) - - for gnc in gn_chords: - chords.append(gnc) - - if not False in type_partition.keys(): - raise KeyError( - "ERROR at ChordNotes-grouping: GraceNotes detected without additional regular Notes at same time; staff " - + str(s) - ) - - reg_notes = type_partition[False] - - rep = reg_notes[0] - - for i in range(1, len(reg_notes)): - n = reg_notes[i] - - if n.duration != rep.duration: - raise ValueError( - "In staff " + str(s) + ",", - "in measure " + str(m.number) + ",", - "for voice " + str(voice) + ",", - "2 notes start at time " + str(n.start.t) + ",", - "but have different durations, namely " - + n.id - + " has duration " - + str(n.duration) - + " and " - + rep.id - + " has duration " - + str(rep.duration), - "change to same duration for a chord or change voice of one of the notes for something else", - ) - # HACK: unpitched notes are treated as Rests right now - elif not isinstance(rep, score.Rest) and not isinstance( - n, score.Rest - ): - if rep.beam != n.beam: - print( - "WARNING: notes within chords don't share the same beam", - "specifically note " - + str(rep) - + " has beam " - + str(rep.beam), - "and note " + str(n) + " has beam " + str(n.beam), - "export still continues though", - ) - elif set(rep.tuplet_starts) != set(n.tuplet_starts) and set( - rep.tuplet_stops - ) != set(n.tuplet_stops): - print( - "WARNING: notes within chords don't share same tuplets, export still continues though" - ) - chords.append(reg_notes) - else: - chords.append(ns) - - tuplet_indices = [] - for tuplet in tuplets: - ci = 0 - start = -1 - stop = -1 - while ci < len(chords): - for n in chords[ci]: - if tuplet in n.tuplet_starts: - start = ci - break - for n in chords[ci]: - if tuplet in n.tuplet_stops: - stop = ci - break - - if start >= 0 and stop >= 0: - if not start <= stop: - raise ValueError( - "In measure " + str(measure_i + 1) + ",", - "in staff " + str(s) + ",", - "[" + str(tuplet) + "] stops before it starts?", - "start=" + str(start + 1) + "; stop=" + str(stop + 1), - ) - tuplet_indices.append((start, stop)) - break - - ci += 1 - - parents = [layer] - open_beam = False - - ( - next_dur_dots, - next_split_notes, - next_first_temp_dur, - ) = calc_dur_dots_split_notes_first_temp_dur( - chords[0][0], m, calc_num_to_numbase_ratio(0, chords, tuplet_indices) - ) - - inbetween_notes_elements = [ - InbetweenNotesElement( - "clef", - ["shape", "line", "dis", "dis.place"], - attribs_of_clef, - current_measure_content.clefs_per_staff, - s, - int(measure_i == 0), - ), - InbetweenNotesElement( - "keySig", - ["sig", "mode", "pname", "sig.showchange"], - (lambda ks: attribs_of_key_sig(ks) + ("true",)), - current_measure_content.key_sigs_per_staff, - s, - int(score_def != None), - ), - InbetweenNotesElement( - "meterSig", - ["count", "unit"], - lambda ts: (ts.beats, ts.beat_type), - current_measure_content.time_sigs_per_staff, - s, - int(score_def != None), - ), - ] - - open_tuplet = False - - notes_next_measure_per_staff = {} - - for chord_i in range(len(chords) - 1): - dur_dots, split_notes, first_temp_dur = ( - next_dur_dots, - next_split_notes, - next_first_temp_dur, - ) - ( - next_dur_dots, - next_split_notes, - next_first_temp_dur, - ) = calc_dur_dots_split_notes_first_temp_dur( - chord_rep(chords, chord_i + 1), - m, - calc_num_to_numbase_ratio(chord_i + 1, chords, tuplet_indices), - ) - tuplet_id_counter, open_beam, open_tuplet = process_chord( - chord_i, - chords, - inbetween_notes_elements, - open_beam, - auto_beaming, - parents, - dur_dots, - split_notes, - first_temp_dur, - tuplet_indices, - ties, - m, - layer, - tuplet_id_counter, - open_tuplet, - last_key_sig, - note_alterations, - notes_next_measure_per_staff, - next_dur_dots, - ) - - tuplet_id_counter, _, _ = process_chord( - len(chords) - 1, - chords, - inbetween_notes_elements, - open_beam, - auto_beaming, - parents, - next_dur_dots, - next_split_notes, - next_first_temp_dur, - tuplet_indices, - ties, - m, - layer, - tuplet_id_counter, - open_tuplet, - last_key_sig, - note_alterations, - notes_next_measure_per_staff, - ) - - ties_per_staff_per_voice[voice] = ties - - ties_per_staff[s] = ties_per_staff_per_voice - - for fermata in current_measure_content.fermatas: - tstamp = fermata[0] - fermata_staff = fermata[1] - - f = add_child(measure, "fermata") - set_attributes(f, ("staff", fermata_staff), ("tstamp", tstamp)) - - for slur in current_measure_content.slurs: - s = add_child(measure, "slur") - if slur.start_note == None or slur.end_note == None: - raise ValueError("Slur is missing start or end") - set_attributes( - s, - ("staff", slur.start_note.staff), - ("startid", "#" + slur.start_note.id), - ("endid", "#" + slur.end_note.id), - ) - - for tstamp, word in current_measure_content.dirs: - d = add_child(measure, "dir") - set_attributes(d, ("staff", word.staff), ("tstamp", tstamp)) - d.text = word.text - - # smufl individual notes start with E1 - # these are the last 2 digits of the codes - metronome_codes = { - "breve": "D0", - "whole": "D2", - "half": "D3", - "h": "D3", - "quarter": "D5", - "q": "D5", - "eighth": "D7", - "e": "D5", - "16th": "D9", - "32nd": "DB", - "64th": "DD", - "128th": "DF", - "256th": "E1", - } - - for tstamp, staff, tempo in current_measure_content.tempii: - t = add_child(measure, "tempo") - set_attributes(t, ("staff", staff), ("tstamp", tstamp)) - - unit = str(tempo.unit) - - dots = unit.count(".") - - unit = unit[:-dots] - - string_to_build = [ - ' á', - metronome_codes[unit or "q"], - ";", - ] - - for i in range(dots): - string_to_build.append("") - - string_to_build.append(" = ") - string_to_build.append(str(tempo.bpm)) - - t.text = "".join(string_to_build) - - for tstamp, tstamp2, dynam in current_measure_content.dynams: - if isinstance(dynam, score.DynamicLoudnessDirection): - d = add_child(measure, "hairpin") - form = ( - "cres" - if isinstance(dynam, score.IncreasingLoudnessDirection) - else "dim" - ) - set_attributes(d, ("form", form)) - - # duration can also matter for other dynamics, might want to move this out of branch - if tstamp2 != None: - set_attributes(d, ("tstamp2", tstamp2)) - else: - d = add_child(measure, "dynam") - d.text = dynam.text - - set_attributes(d, ("staff", dynam.staff), ("tstamp", tstamp)) - - for s, tps in ties_per_staff.items(): - - for v, tpspv in tps.items(): - - for ties in tpspv.values(): - - for i in range(len(ties) - 1): - tie = add_child(measure, "tie") - - set_attributes( - tie, - ("staff", s), - ("startid", "#" + ties[i]), - ("endid", "#" + ties[i + 1]), - ) - - for s, k in current_measure_content.key_sigs_per_staff.items(): - if len(k) > 0: - last_key_sig_per_staff[s] = max(k, key=lambda k: k.start.t) - - return tuplet_id_counter, notes_next_measure_per_staff - - -def unpack_part_group(part_grp, parts=[]): - """ - Recursively gather individual parts into a list, flattening the tree of parts so to say - - Parameters - ---------- - part_grp: score.PartGroup - parts: list of score.Part, optional - - Returns - ------- - parts: list of score.Part - """ - for c in part_grp.children: - if isinstance(c, score.PartGroup): - unpack_part_group(c, parts) - else: - parts.append(c) - - return parts - - -def save_mei( - parts, - auto_beaming=True, - file_name="testResult", - title_text=None, - proper_staff_grp=False, -): - """ - creates an MEI document based on the parts provided - So far only is used and not which means all the parts are gathered in one whole score and - no individual scores are defined for individual parts - - Parameters - ---------- - parts: score.Part, score.PartGroup or list of score.Part - auto_beaming: boolean, optional - if all beaming has been done manually then set to False - otherwise this flag can be used to enable automatic beaming (beaming rules are still in progess) - file_name: string, optional - should not contain file extension, .mei will be added automatically - title_text: string, optional - name of the piece, e.g. "Klaviersonate Nr. 14" or "WAP" - if not provided, a title will be derived from file_name - proper_staff_grp: boolean, optional - if true, group staves per part - else group all staves together - default is false because Verovio doesn't seem to render multiple staff groups correctly (but that just might be because multiple staff groups are not generated correctly in this function) - """ - - if isinstance(parts, score.PartGroup): - parts = unpack_part_group(parts) - elif isinstance(parts, score.Part): - parts = [parts] - - for p in parts: - score.sanitize_part(p) - - mei = etree.Element("mei") - - mei_head = add_child(mei, "meiHead") - music = add_child(mei, "music") - - mei_head.set("xmlns", name_space) - file_desc = add_child(mei_head, "fileDesc") - title_stmt = add_child(file_desc, "titleStmt") - pub_stmt = add_child(file_desc, "pubStmt") - title = add_child(title_stmt, "title") - title.set("type", "main") - - # derive a title for the piece from the file_name - if title_text == None: - cursor = len(file_name) - 1 - while cursor >= 0 and file_name[cursor] != "/": - cursor -= 1 - - tmp = file_name[cursor + 1 :].split("_") - tmp = [s[:1].upper() + s[1:] for s in tmp] - title.text = " ".join(tmp) - else: - title.text = title_text - - body = add_child(music, "body") - mdiv = add_child(body, "mdiv") - mei_score = add_child(mdiv, "score") - - classes_with_staff = [score.GenericNote, score.Words, score.Direction] - - staves_per_part = [] - - staves_are_valid = True - - for p in parts: - tmp = { - staffed_obj.staff - for cls in classes_with_staff - for staffed_obj in p.iter_all(cls, include_subclasses=True) - } - tmp = tmp.union({clef.number for clef in p.iter_all(score.Clef)}) - staves_per_part.append(list(tmp)) - - if None in staves_per_part[-1]: - staves_are_valid = False - staves_per_part[-1].remove(None) - - staves_per_part[-1].append( - (max(staves_per_part[-1]) if len(staves_per_part[-1]) > 0 else 0) + 1 - ) - - staves_per_part[-1].sort() - - if staves_are_valid: - staves_sorted = sorted([s for staves in staves_per_part for s in staves]) - - i = 0 - - while i + 1 < len(staves_sorted): - if staves_sorted[i] == staves_sorted[i + 1]: - staves_are_valid = False - break - - i += 1 - - if not staves_are_valid: - staves_per_part_backup = staves_per_part - - staves_sorted = [] - staves_per_part = [] - - # ASSUMPTION: staves are >0 - max_staff = 0 - for staves in staves_per_part_backup: - if len(staves) == 0: - staves_per_part.append([]) - else: - shift = [s + max_staff for s in staves] - - max_staff += max(staves) - - staves_sorted.extend(shift) - staves_per_part.append(shift) - - # staves_sorted.sort() - - max_staff = 0 - for i, p in enumerate(parts): - for cls in classes_with_staff: - for staff_obj in p.iter_all(cls, include_subclasses=True): - staff_obj.staff = max_staff + ( - staff_obj.staff - if staff_obj.staff != None - else max(staves_per_part_backup[i]) - ) - - for clef in p.iter_all(score.Clef): - clef.number = max_staff + ( - clef.number - if clef.number != None - else max(staves_per_part_backup[i]) - ) - - max_staff += ( - max(staves_per_part_backup[i]) - if len(staves_per_part_backup[i]) > 0 - else 0 - ) - - measures = [list(parts[0].iter_all(score.Measure))] - padding_required = False - max_length = len(measures[0]) - for i in range(1, len(parts)): - m = list(parts[i].iter_all(score.Measure)) - - if len(m) > max_length: - max_length = len(m) - - if not padding_required: - padding_required = len(m) != len(measures[0]) - - measures.append(m) - - score_def = create_score_def(measures, 0, parts, mei_score) - - score_def_setup = score_def - - if score_def == None: - score_def_setup = add_child(mei_score, "scoreDef") - - clefs_per_part = first_instances_per_part(score.Clef, parts) - - for i in idx(clefs_per_part): - clefs_per_part[i] = partition_handle_none( - lambda c: c.number, clefs_per_part[i], "number" - ) - - if len(clefs_per_part) == 0: - create_staff_def( - staff_grp, score.Clef(sign="G", line=2, number=1, octave_change=0) - ) - else: - staff_grp = add_child(score_def_setup, "staffGrp") - for staves in staves_per_part: - if proper_staff_grp: - staff_grp = add_child(score_def_setup, "staffGrp") - - for s in staves: - clefs = None - - for clefs_per_staff in clefs_per_part: - if s in clefs_per_staff.keys(): - clefs = clefs_per_staff[s] - break - - if clefs != None: - clef = clefs[0] - if len(clefs) != 1: - raise ValueError( - "ERROR at staff_def creation: Staff " - + str(clef.number) - + " starts with more than 1 clef at t=0" - ) - create_staff_def(staff_grp, clef) - else: - create_staff_def( - staff_grp, - score.Clef(sign="G", line=2, number=s, octave_change=0), - ) - - section = add_child(mei_score, "section") - - measures_are_aligned = True - if padding_required: - cursors = [0] * len(measures) - tempii = [None] * len(measures) - - while measures_are_aligned: - compare_measures = {} - for i, m in enumerate(measures): - if cursors[i] < len(m): - compare_measures[i] = m[cursors[i]] - cursors[i] += 1 - - if len(compare_measures) == 0: - break - - compm_keys = list(compare_measures.keys()) - - new_tempii = first_instance_per_part( - score.Tempo, - [p for i, p in enumerate(parts) if i in compm_keys], - start=[cm.start for cm in compare_measures.values()], - end=[cm.end for cm in compare_measures.values()], - ) - - if len(new_tempii) == 0: - for k in compm_keys: - new_tempii.append(tempii[k]) - else: - for i, nt in enumerate(new_tempii): - if nt == None: - new_tempii[i] = tempii[compm_keys[i]] - else: - tempii[compm_keys[i]] = nt - - def norm_dur(m): - return (m.end.t - m.start.t) // m.start.quarter - - rep_i = 0 - while rep_i < len(new_tempii) and new_tempii[rep_i] == None: - rep_i += 1 - - if rep_i == len(new_tempii): - continue - - rep_dur = ( - norm_dur(compare_measures[compm_keys[rep_i]]) * new_tempii[rep_i].bpm - ) - - for i in range(rep_i + 1, len(compm_keys)): - nt = new_tempii[i] - - if nt == None: - continue - - m = compare_measures[compm_keys[i]] - dur = norm_dur(m) * new_tempii[i].bpm - - if dur != rep_dur: - measures_are_aligned = False - break - - tuplet_id_counter = 0 - - if measures_are_aligned: - time_offset = [0] * len(measures) - - if padding_required: - for i, mp in enumerate(measures): - ii = len(mp) - time_offset[i] = mp[ii - 1].end.t - while ii < max_length: - mp.append("pad") - ii += 1 - - notes_last_measure_per_staff = {} - auto_rest_count = 0 - - notes_within_measure_per_staff = notes_last_measure_per_staff - - auto_rest_count, current_measure_content = extract_from_measures( - parts, - measures, - 0, - staves_per_part, - auto_rest_count, - notes_within_measure_per_staff, - ) - - last_key_sig_per_staff = {} - - for s, k in current_measure_content.key_sigs_per_staff.items(): - last_key_sig_per_staff[s] = ( - min(k, key=lambda k: k.start.t) if len(k) > 0 else None - ) - - tuplet_id_counter, notes_last_measure_per_staff = create_measure( - section, - 0, - staves_sorted, - notes_within_measure_per_staff, - score_def, - tuplet_id_counter, - auto_beaming, - last_key_sig_per_staff, - current_measure_content, - ) - - for measure_i in range(1, len(measures[0])): - notes_within_measure_per_staff = notes_last_measure_per_staff - - auto_rest_count, current_measure_content = extract_from_measures( - parts, - measures, - measure_i, - staves_per_part, - auto_rest_count, - notes_within_measure_per_staff, - ) - - score_def = create_score_def(measures, measure_i, parts, section) - - tuplet_id_counter, notes_last_measure_per_staff = create_measure( - section, - measure_i, - staves_sorted, - notes_within_measure_per_staff, - score_def, - tuplet_id_counter, - auto_beaming, - last_key_sig_per_staff, - current_measure_content, - ) - - (etree.ElementTree(mei)).write(file_name + ".mei", pretty_print=True) - - # post processing step necessary - # etree won't write <,> and & into an element's text - with open(file_name + ".mei") as result: - text = list(result.read()) - new_text = [] - - i = 0 - while i < len(text): - ch = text[i] - if ch == "&": - if text[i + 1 : i + 4] == ["l", "t", ";"]: - ch = "<" - i += 4 - elif text[i + 1 : i + 4] == ["g", "t", ";"]: - ch = ">" - i += 4 - elif text[i + 1 : i + 5] == ["a", "m", "p", ";"]: - i += 5 - else: - i += 1 - else: - i += 1 - - new_text.append(ch) - - new_text = "".join(new_text) - - with open(file_name + ".mei", "w") as result: - result.write(new_text) +# for n in chord_notes: +# if n.tie_next != None: +# if n.id in ties.keys(): +# ties[n.id].append(n.tie_next.id) +# else: +# ties[n.id] = [n.id, n.tie_next.id] + +# elif isinstance(rep, score.Rest): +# if split_notes != None: +# split_notes.append(score.Rest(id=rep.id + "s")) + +# if ( +# measure == "pad" +# or measure.start.t == rep.start.t +# and measure.end.t == rep.end.t +# ): +# rest = add_child(layer, "mRest") + +# set_attributes(rest, (xml_id_string, rep.id)) +# else: +# rest = add_child(layer, "rest") + +# set_attributes(rest, (xml_id_string, rep.id)) + +# set_dur_dots(rest, dur_dots[0]) + +# for i in range(1, len(dur_dots)): +# rest = add_child(layer, "rest") + +# id = rep.id + str(i) + +# set_attributes(rest, (xml_id_string, id)) +# set_dur_dots(rest, dur_dots[i]) + +# if split_notes != None: +# for sn in split_notes: +# sn.voice = rep.voice +# sn.start = measure.end +# sn.end = score.TimePoint(rep.start.t + rep.duration) + +# extend_key(notes_next_measure_per_staff, s, sn) + +# return tuplet_id_counter, open_beam, open_tuplet + + +# def create_score_def(measures, measure_i, parts, parent): +# """ +# creates + +# Parameters +# ---------- +# measures: list of score.Measure +# measure_i: int +# index of measure currently processed within measures +# parts: list of score.Part +# parent: etree.SubElement +# parent of +# """ +# reference_measures = vertical_slice(measures, measure_i) + +# common_key_sig = common_signature( +# score.KeySignature, key_sig_eql, parts, reference_measures +# ) +# common_time_sig = common_signature( +# score.TimeSignature, time_sig_eql, parts, reference_measures +# ) + +# score_def = None + +# if common_key_sig != None or common_time_sig != None: +# score_def = add_child(parent, "scoreDef") + +# if common_key_sig != None: +# fifths, mode, pname = attribs_of_key_sig(common_key_sig) + +# set_attributes( +# score_def, ("key.sig", fifths), ("key.mode", mode), ("key.pname", pname) +# ) + +# if common_time_sig != None: +# set_attributes( +# score_def, +# ("meter.count", common_time_sig.beats), +# ("meter.unit", common_time_sig.beat_type), +# ) + +# return score_def + + +# class MeasureContent: +# """ +# Simply a bundle for all the data of a measure that needs to be processed for a MEI document + +# Attributes +# ---------- +# ties_per_staff: dict of lists +# clefs_per_staff: dict of lists +# key_sigs_per_staff: dict of lists +# time_sigs_per_staff: dict of lists +# measure_per_staff: dict of lists +# tuplets_per_staff: dict of lists +# slurs: list +# dirs: list +# dynams: list +# tempii: list +# fermatas: list +# """ + +# __slots__ = [ +# "ties_per_staff", +# "clefs_per_staff", +# "key_sigs_per_staff", +# "time_sigs_per_staff", +# "measure_per_staff", +# "tuplets_per_staff", +# "slurs", +# "dirs", +# "dynams", +# "tempii", +# "fermatas", +# ] + +# def __init__(self): +# self.ties_per_staff = {} +# self.clefs_per_staff = {} +# self.key_sigs_per_staff = {} +# self.time_sigs_per_staff = {} +# self.measure_per_staff = {} +# self.tuplets_per_staff = {} + +# self.slurs = [] +# self.dirs = [] +# self.dynams = [] +# self.tempii = [] +# self.fermatas = [] + + +# def extract_from_measures( +# parts, +# measures, +# measure_i, +# staves_per_part, +# auto_rest_count, +# notes_within_measure_per_staff, +# ): +# """ +# Returns a bundle of data regarding the measure currently processed, things like notes, key signatures, etc +# Also creates padding measures, necessary for example, for staves of instruments which do not play in the current measure + +# Parameters +# ---------- +# parts: list of score.Part +# measures: list of score.Measure +# measure_i: int +# index of current measure within measures +# staves_per_part: dict of list of ints +# staff enumeration partitioned by part +# auto_rest_count: int +# counter for the IDs of automatically generated rests +# notes_within_measure_per_staff: dict of lists of score.GenericNote +# in and out parameter, might contain note objects that have crossed from previous measure into current one + +# Returns +# ------- +# auto_rest_count: int +# incremented if score.Rest created +# current_measure_content: MeasureContent +# bundle for all the data that is extracted from the currently processed measure +# """ +# current_measure_content = MeasureContent() + +# for part_i, part in enumerate(parts): +# m = measures[part_i][measure_i] + +# if m == "pad": +# for s in staves_per_part[part_i]: +# auto_rest_count = pad_measure( +# s, +# current_measure_content.measure_per_staff, +# notes_within_measure_per_staff, +# auto_rest_count, +# ) + +# continue + +# def cls_within_measure(part, cls, measure, incl_subcls=False): +# return part.iter_all( +# cls, measure.start, measure.end, include_subclasses=incl_subcls +# ) + +# def cls_within_measure_list(part, cls, measure, incl_subcls=False): +# return list(cls_within_measure(part, cls, measure, incl_subcls)) + +# clefs_within_measure_per_staff_per_part = partition_handle_none( +# lambda c: c.number, cls_within_measure(part, score.Clef, m), "number" +# ) +# key_sigs_within_measure = cls_within_measure_list(part, score.KeySignature, m) +# time_sigs_within_measure = cls_within_measure_list(part, score.TimeSignature, m) +# current_measure_content.slurs.extend(cls_within_measure(part, score.Slur, m)) +# tuplets_within_measure = cls_within_measure_list(part, score.Tuplet, m) + +# beat_map = part.beat_map + +# def calc_tstamp(beat_map, t, measure): +# return beat_map(t) - beat_map(measure.start.t) + 1 + +# for w in cls_within_measure(part, score.Words, m): +# tstamp = calc_tstamp(beat_map, w.start.t, m) +# current_measure_content.dirs.append((tstamp, w)) + +# for tempo in cls_within_measure(part, score.Tempo, m): +# tstamp = calc_tstamp(beat_map, tempo.start.t, m) +# current_measure_content.tempii.append( +# (tstamp, staves_per_part[part_i][0], tempo) +# ) + +# for fermata in cls_within_measure(part, score.Fermata, m): +# tstamp = calc_tstamp(beat_map, fermata.start.t, m) +# current_measure_content.fermatas.append((tstamp, fermata.ref.staff)) + +# for dynam in cls_within_measure(part, score.Direction, m, True): +# tstamp = calc_tstamp(beat_map, dynam.start.t, m) +# tstamp2 = None + +# if dynam.end != None: +# measure_counter = measure_i +# while True: +# if dynam.end.t <= measures[part_i][measure_counter].end.t: +# tstamp2 = calc_tstamp( +# beat_map, dynam.end.t, measures[part_i][measure_counter] +# ) + +# tstamp2 = str(measure_counter - measure_i) + "m+" + str(tstamp2) + +# break +# elif ( +# measure_counter + 1 >= len(measures[part_i]) +# or measures[part_i][measure_counter + 1] == "pad" +# ): +# raise ValueError( +# "A score.Direction instance has an end time that exceeds actual non-padded measures" +# ) +# else: +# measure_counter += 1 + +# current_measure_content.dynams.append((tstamp, tstamp2, dynam)) + +# notes_within_measure_per_staff_per_part = partition_handle_none( +# lambda n: n.staff, +# cls_within_measure(part, score.GenericNote, m, True), +# "staff", +# ) + +# for s in staves_per_part[part_i]: +# current_measure_content.key_sigs_per_staff[s] = key_sigs_within_measure +# current_measure_content.time_sigs_per_staff[s] = time_sigs_within_measure +# current_measure_content.tuplets_per_staff[s] = tuplets_within_measure + +# if s not in notes_within_measure_per_staff_per_part.keys(): +# auto_rest_count = pad_measure( +# s, +# current_measure_content.measure_per_staff, +# notes_within_measure_per_staff, +# auto_rest_count, +# ) + +# for s, nwp in notes_within_measure_per_staff_per_part.items(): +# extend_key(notes_within_measure_per_staff, s, nwp) +# current_measure_content.measure_per_staff[s] = m + +# for s, cwp in clefs_within_measure_per_staff_per_part.items(): +# current_measure_content.clefs_per_staff[s] = cwp + +# return auto_rest_count, current_measure_content + + +# def create_measure( +# section, +# measure_i, +# staves_sorted, +# notes_within_measure_per_staff, +# score_def, +# tuplet_id_counter, +# auto_beaming, +# last_key_sig_per_staff, +# current_measure_content, +# ): +# """ +# creates a element within
+# also returns an updated id counter for tuplets and a dictionary of notes that cross into the next measure + +# Parameters +# ---------- +# section: etree.SubElement +# measure_i: int +# index of the measure created +# staves_sorted: list of ints +# a sorted list of the proper staff enumeration of the score +# notes_within_measure_per_staff: dict of lists of score.GenericNote +# contains score.Note, score.Rest, etc objects of the current measure, partitioned by staff enumeration +# will be further partitioned and sorted by voice, time and type (score.GraceNote) and eventually gathered into +# a list of equivalence classes called chords +# score_def: etree.SubElement +# tuplet_id_counter: int +# tuplets usually don't come with IDs, so an automatic counter takes care of that +# auto_beaming: boolean +# enables automatic beaming +# last_key_sig_per_staff: dict of score.KeySignature +# keeps track of the keysignature each staff is currently in +# current_measure_content: MeasureContent +# contains all sorts of data for the measure like tuplets, slurs, etc + +# Returns +# ------- +# tuplet_id_counter: int +# incremented if tuplet created +# notes_next_measure_per_staff: dict of lists of score.GenericNote +# score.GenericNote objects that cross into the next measure +# """ +# measure = add_child(section, "measure") +# set_attributes(measure, ("n", measure_i + 1)) + +# ties_per_staff = {} + +# for s in staves_sorted: +# note_alterations = {} + +# staff = add_child(measure, "staff") + +# set_attributes(staff, ("n", s)) + +# notes_within_measure_per_staff_per_voice = partition_handle_none( +# lambda n: n.voice, notes_within_measure_per_staff[s], "voice" +# ) + +# ties_per_staff_per_voice = {} + +# m = current_measure_content.measure_per_staff[s] + +# tuplets = [] +# if s in current_measure_content.tuplets_per_staff.keys(): +# tuplets = current_measure_content.tuplets_per_staff[s] + +# last_key_sig = last_key_sig_per_staff[s] + +# for voice, notes in notes_within_measure_per_staff_per_voice.items(): +# layer = add_child(staff, "layer") + +# set_attributes(layer, ("n", voice)) + +# ties = {} + +# notes_partition = partition_handle_none( +# lambda n: n.start.t, notes, "start.t" +# ) + +# chords = [] + +# for t in sorted(notes_partition.keys()): +# ns = notes_partition[t] + +# if len(ns) > 1: +# type_partition = partition_handle_none( +# lambda n: isinstance(n, score.GraceNote), ns, "isGraceNote" +# ) + +# if True in type_partition.keys(): +# gns = type_partition[True] + +# gn_chords = [] + +# def scan_backwards(gns): +# start = gns[0] + +# while isinstance(start.grace_prev, score.GraceNote): +# start = start.grace_prev + +# return start + +# start = scan_backwards(gns) + +# def process_grace_note(n, gns): +# if not n in gns: +# raise ValueError( +# "Error at forward scan of GraceNotes: a grace_next has either different staff, voice or starting time than GraceNote chain" +# ) +# gns.remove(n) +# return n.grace_next + +# while isinstance(start, score.GraceNote): +# gn_chords.append([start]) +# start = process_grace_note(start, gns) + +# while len(gns) > 0: +# start = scan_backwards(gns) + +# i = 0 +# while isinstance(start, score.GraceNote): +# if i >= len(gn_chords): +# raise IndexError( +# "ERROR at GraceNote-forward scanning: Difference in lengths of grace note sequences for different chord notes" +# ) +# gn_chords[i].append(start) +# start = process_grace_note(start, gns) +# i += 1 + +# if not i == len(gn_chords): +# raise IndexError( +# "ERROR at GraceNote-forward scanning: Difference in lengths of grace note sequences for different chord notes" +# ) + +# for gnc in gn_chords: +# chords.append(gnc) + +# if not False in type_partition.keys(): +# raise KeyError( +# "ERROR at ChordNotes-grouping: GraceNotes detected without additional regular Notes at same time; staff " +# + str(s) +# ) + +# reg_notes = type_partition[False] + +# rep = reg_notes[0] + +# for i in range(1, len(reg_notes)): +# n = reg_notes[i] + +# if n.duration != rep.duration: +# raise ValueError( +# "In staff " + str(s) + ",", +# "in measure " + str(m.number) + ",", +# "for voice " + str(voice) + ",", +# "2 notes start at time " + str(n.start.t) + ",", +# "but have different durations, namely " +# + n.id +# + " has duration " +# + str(n.duration) +# + " and " +# + rep.id +# + " has duration " +# + str(rep.duration), +# "change to same duration for a chord or change voice of one of the notes for something else", +# ) +# # HACK: unpitched notes are treated as Rests right now +# elif not isinstance(rep, score.Rest) and not isinstance( +# n, score.Rest +# ): +# if rep.beam != n.beam: +# print( +# "WARNING: notes within chords don't share the same beam", +# "specifically note " +# + str(rep) +# + " has beam " +# + str(rep.beam), +# "and note " + str(n) + " has beam " + str(n.beam), +# "export still continues though", +# ) +# elif set(rep.tuplet_starts) != set(n.tuplet_starts) and set( +# rep.tuplet_stops +# ) != set(n.tuplet_stops): +# print( +# "WARNING: notes within chords don't share same tuplets, export still continues though" +# ) +# chords.append(reg_notes) +# else: +# chords.append(ns) + +# tuplet_indices = [] +# for tuplet in tuplets: +# ci = 0 +# start = -1 +# stop = -1 +# while ci < len(chords): +# for n in chords[ci]: +# if tuplet in n.tuplet_starts: +# start = ci +# break +# for n in chords[ci]: +# if tuplet in n.tuplet_stops: +# stop = ci +# break + +# if start >= 0 and stop >= 0: +# if not start <= stop: +# raise ValueError( +# "In measure " + str(measure_i + 1) + ",", +# "in staff " + str(s) + ",", +# "[" + str(tuplet) + "] stops before it starts?", +# "start=" + str(start + 1) + "; stop=" + str(stop + 1), +# ) +# tuplet_indices.append((start, stop)) +# break + +# ci += 1 + +# parents = [layer] +# open_beam = False + +# ( +# next_dur_dots, +# next_split_notes, +# next_first_temp_dur, +# ) = calc_dur_dots_split_notes_first_temp_dur( +# chords[0][0], m, calc_num_to_numbase_ratio(0, chords, tuplet_indices) +# ) + +# inbetween_notes_elements = [ +# InbetweenNotesElement( +# "clef", +# ["shape", "line", "dis", "dis.place"], +# attribs_of_clef, +# current_measure_content.clefs_per_staff, +# s, +# int(measure_i == 0), +# ), +# InbetweenNotesElement( +# "keySig", +# ["sig", "mode", "pname", "sig.showchange"], +# (lambda ks: attribs_of_key_sig(ks) + ("true",)), +# current_measure_content.key_sigs_per_staff, +# s, +# int(score_def != None), +# ), +# InbetweenNotesElement( +# "meterSig", +# ["count", "unit"], +# lambda ts: (ts.beats, ts.beat_type), +# current_measure_content.time_sigs_per_staff, +# s, +# int(score_def != None), +# ), +# ] + +# open_tuplet = False + +# notes_next_measure_per_staff = {} + +# for chord_i in range(len(chords) - 1): +# dur_dots, split_notes, first_temp_dur = ( +# next_dur_dots, +# next_split_notes, +# next_first_temp_dur, +# ) +# ( +# next_dur_dots, +# next_split_notes, +# next_first_temp_dur, +# ) = calc_dur_dots_split_notes_first_temp_dur( +# chord_rep(chords, chord_i + 1), +# m, +# calc_num_to_numbase_ratio(chord_i + 1, chords, tuplet_indices), +# ) +# tuplet_id_counter, open_beam, open_tuplet = process_chord( +# chord_i, +# chords, +# inbetween_notes_elements, +# open_beam, +# auto_beaming, +# parents, +# dur_dots, +# split_notes, +# first_temp_dur, +# tuplet_indices, +# ties, +# m, +# layer, +# tuplet_id_counter, +# open_tuplet, +# last_key_sig, +# note_alterations, +# notes_next_measure_per_staff, +# next_dur_dots, +# ) + +# tuplet_id_counter, _, _ = process_chord( +# len(chords) - 1, +# chords, +# inbetween_notes_elements, +# open_beam, +# auto_beaming, +# parents, +# next_dur_dots, +# next_split_notes, +# next_first_temp_dur, +# tuplet_indices, +# ties, +# m, +# layer, +# tuplet_id_counter, +# open_tuplet, +# last_key_sig, +# note_alterations, +# notes_next_measure_per_staff, +# ) + +# ties_per_staff_per_voice[voice] = ties + +# ties_per_staff[s] = ties_per_staff_per_voice + +# for fermata in current_measure_content.fermatas: +# tstamp = fermata[0] +# fermata_staff = fermata[1] + +# f = add_child(measure, "fermata") +# set_attributes(f, ("staff", fermata_staff), ("tstamp", tstamp)) + +# for slur in current_measure_content.slurs: +# s = add_child(measure, "slur") +# if slur.start_note == None or slur.end_note == None: +# raise ValueError("Slur is missing start or end") +# set_attributes( +# s, +# ("staff", slur.start_note.staff), +# ("startid", "#" + slur.start_note.id), +# ("endid", "#" + slur.end_note.id), +# ) + +# for tstamp, word in current_measure_content.dirs: +# d = add_child(measure, "dir") +# set_attributes(d, ("staff", word.staff), ("tstamp", tstamp)) +# d.text = word.text + +# # smufl individual notes start with E1 +# # these are the last 2 digits of the codes +# metronome_codes = { +# "breve": "D0", +# "whole": "D2", +# "half": "D3", +# "h": "D3", +# "quarter": "D5", +# "q": "D5", +# "eighth": "D7", +# "e": "D5", +# "16th": "D9", +# "32nd": "DB", +# "64th": "DD", +# "128th": "DF", +# "256th": "E1", +# } + +# for tstamp, staff, tempo in current_measure_content.tempii: +# t = add_child(measure, "tempo") +# set_attributes(t, ("staff", staff), ("tstamp", tstamp)) + +# unit = str(tempo.unit) + +# dots = unit.count(".") + +# unit = unit[:-dots] + +# string_to_build = [ +# ' á', +# metronome_codes[unit or "q"], +# ";", +# ] + +# for i in range(dots): +# string_to_build.append("") + +# string_to_build.append(" = ") +# string_to_build.append(str(tempo.bpm)) + +# t.text = "".join(string_to_build) + +# for tstamp, tstamp2, dynam in current_measure_content.dynams: +# if isinstance(dynam, score.DynamicLoudnessDirection): +# d = add_child(measure, "hairpin") +# form = ( +# "cres" +# if isinstance(dynam, score.IncreasingLoudnessDirection) +# else "dim" +# ) +# set_attributes(d, ("form", form)) + +# # duration can also matter for other dynamics, might want to move this out of branch +# if tstamp2 != None: +# set_attributes(d, ("tstamp2", tstamp2)) +# else: +# d = add_child(measure, "dynam") +# d.text = dynam.text + +# set_attributes(d, ("staff", dynam.staff), ("tstamp", tstamp)) + +# for s, tps in ties_per_staff.items(): + +# for v, tpspv in tps.items(): + +# for ties in tpspv.values(): + +# for i in range(len(ties) - 1): +# tie = add_child(measure, "tie") + +# set_attributes( +# tie, +# ("staff", s), +# ("startid", "#" + ties[i]), +# ("endid", "#" + ties[i + 1]), +# ) + +# for s, k in current_measure_content.key_sigs_per_staff.items(): +# if len(k) > 0: +# last_key_sig_per_staff[s] = max(k, key=lambda k: k.start.t) + +# return tuplet_id_counter, notes_next_measure_per_staff + + +# def unpack_part_group(part_grp, parts=[]): +# """ +# Recursively gather individual parts into a list, flattening the tree of parts so to say + +# Parameters +# ---------- +# part_grp: score.PartGroup +# parts: list of score.Part, optional + +# Returns +# ------- +# parts: list of score.Part +# """ +# for c in part_grp.children: +# if isinstance(c, score.PartGroup): +# unpack_part_group(c, parts) +# else: +# parts.append(c) + +# return parts + + +# def save_mei( +# parts, +# auto_beaming=True, +# file_name="testResult", +# title_text=None, +# proper_staff_grp=False, +# ): +# """ +# creates an MEI document based on the parts provided +# So far only is used and not which means all the parts are gathered in one whole score and +# no individual scores are defined for individual parts + +# Parameters +# ---------- +# parts: score.Part, score.PartGroup or list of score.Part +# auto_beaming: boolean, optional +# if all beaming has been done manually then set to False +# otherwise this flag can be used to enable automatic beaming (beaming rules are still in progess) +# file_name: string, optional +# should not contain file extension, .mei will be added automatically +# title_text: string, optional +# name of the piece, e.g. "Klaviersonate Nr. 14" or "WAP" +# if not provided, a title will be derived from file_name +# proper_staff_grp: boolean, optional +# if true, group staves per part +# else group all staves together +# default is false because Verovio doesn't seem to render multiple staff groups correctly (but that just might be because multiple staff groups are not generated correctly in this function) +# """ + +# if isinstance(parts, score.PartGroup): +# parts = unpack_part_group(parts) +# elif isinstance(parts, score.Part): +# parts = [parts] + +# for p in parts: +# score.sanitize_part(p) + +# mei = etree.Element("mei") + +# mei_head = add_child(mei, "meiHead") +# music = add_child(mei, "music") + +# mei_head.set("xmlns", name_space) +# file_desc = add_child(mei_head, "fileDesc") +# title_stmt = add_child(file_desc, "titleStmt") +# pub_stmt = add_child(file_desc, "pubStmt") +# title = add_child(title_stmt, "title") +# title.set("type", "main") + +# # derive a title for the piece from the file_name +# if title_text == None: +# cursor = len(file_name) - 1 +# while cursor >= 0 and file_name[cursor] != "/": +# cursor -= 1 + +# tmp = file_name[cursor + 1 :].split("_") +# tmp = [s[:1].upper() + s[1:] for s in tmp] +# title.text = " ".join(tmp) +# else: +# title.text = title_text + +# body = add_child(music, "body") +# mdiv = add_child(body, "mdiv") +# mei_score = add_child(mdiv, "score") + +# classes_with_staff = [score.GenericNote, score.Words, score.Direction] + +# staves_per_part = [] + +# staves_are_valid = True + +# for p in parts: +# tmp = { +# staffed_obj.staff +# for cls in classes_with_staff +# for staffed_obj in p.iter_all(cls, include_subclasses=True) +# } +# tmp = tmp.union({clef.number for clef in p.iter_all(score.Clef)}) +# staves_per_part.append(list(tmp)) + +# if None in staves_per_part[-1]: +# staves_are_valid = False +# staves_per_part[-1].remove(None) + +# staves_per_part[-1].append( +# (max(staves_per_part[-1]) if len(staves_per_part[-1]) > 0 else 0) + 1 +# ) + +# staves_per_part[-1].sort() + +# if staves_are_valid: +# staves_sorted = sorted([s for staves in staves_per_part for s in staves]) + +# i = 0 + +# while i + 1 < len(staves_sorted): +# if staves_sorted[i] == staves_sorted[i + 1]: +# staves_are_valid = False +# break + +# i += 1 + +# if not staves_are_valid: +# staves_per_part_backup = staves_per_part + +# staves_sorted = [] +# staves_per_part = [] + +# # ASSUMPTION: staves are >0 +# max_staff = 0 +# for staves in staves_per_part_backup: +# if len(staves) == 0: +# staves_per_part.append([]) +# else: +# shift = [s + max_staff for s in staves] + +# max_staff += max(staves) + +# staves_sorted.extend(shift) +# staves_per_part.append(shift) + +# # staves_sorted.sort() + +# max_staff = 0 +# for i, p in enumerate(parts): +# for cls in classes_with_staff: +# for staff_obj in p.iter_all(cls, include_subclasses=True): +# staff_obj.staff = max_staff + ( +# staff_obj.staff +# if staff_obj.staff != None +# else max(staves_per_part_backup[i]) +# ) + +# for clef in p.iter_all(score.Clef): +# clef.number = max_staff + ( +# clef.number +# if clef.number != None +# else max(staves_per_part_backup[i]) +# ) + +# max_staff += ( +# max(staves_per_part_backup[i]) +# if len(staves_per_part_backup[i]) > 0 +# else 0 +# ) + +# measures = [list(parts[0].iter_all(score.Measure))] +# padding_required = False +# max_length = len(measures[0]) +# for i in range(1, len(parts)): +# m = list(parts[i].iter_all(score.Measure)) + +# if len(m) > max_length: +# max_length = len(m) + +# if not padding_required: +# padding_required = len(m) != len(measures[0]) + +# measures.append(m) + +# score_def = create_score_def(measures, 0, parts, mei_score) + +# score_def_setup = score_def + +# if score_def == None: +# score_def_setup = add_child(mei_score, "scoreDef") + +# clefs_per_part = first_instances_per_part(score.Clef, parts) + +# for i in idx(clefs_per_part): +# clefs_per_part[i] = partition_handle_none( +# lambda c: c.number, clefs_per_part[i], "number" +# ) + +# if len(clefs_per_part) == 0: +# create_staff_def( +# staff_grp, score.Clef(sign="G", line=2, number=1, octave_change=0) +# ) +# else: +# staff_grp = add_child(score_def_setup, "staffGrp") +# for staves in staves_per_part: +# if proper_staff_grp: +# staff_grp = add_child(score_def_setup, "staffGrp") + +# for s in staves: +# clefs = None + +# for clefs_per_staff in clefs_per_part: +# if s in clefs_per_staff.keys(): +# clefs = clefs_per_staff[s] +# break + +# if clefs != None: +# clef = clefs[0] +# if len(clefs) != 1: +# raise ValueError( +# "ERROR at staff_def creation: Staff " +# + str(clef.number) +# + " starts with more than 1 clef at t=0" +# ) +# create_staff_def(staff_grp, clef) +# else: +# create_staff_def( +# staff_grp, +# score.Clef(sign="G", line=2, number=s, octave_change=0), +# ) + +# section = add_child(mei_score, "section") + +# measures_are_aligned = True +# if padding_required: +# cursors = [0] * len(measures) +# tempii = [None] * len(measures) + +# while measures_are_aligned: +# compare_measures = {} +# for i, m in enumerate(measures): +# if cursors[i] < len(m): +# compare_measures[i] = m[cursors[i]] +# cursors[i] += 1 + +# if len(compare_measures) == 0: +# break + +# compm_keys = list(compare_measures.keys()) + +# new_tempii = first_instance_per_part( +# score.Tempo, +# [p for i, p in enumerate(parts) if i in compm_keys], +# start=[cm.start for cm in compare_measures.values()], +# end=[cm.end for cm in compare_measures.values()], +# ) + +# if len(new_tempii) == 0: +# for k in compm_keys: +# new_tempii.append(tempii[k]) +# else: +# for i, nt in enumerate(new_tempii): +# if nt == None: +# new_tempii[i] = tempii[compm_keys[i]] +# else: +# tempii[compm_keys[i]] = nt + +# def norm_dur(m): +# return (m.end.t - m.start.t) // m.start.quarter + +# rep_i = 0 +# while rep_i < len(new_tempii) and new_tempii[rep_i] == None: +# rep_i += 1 + +# if rep_i == len(new_tempii): +# continue + +# rep_dur = ( +# norm_dur(compare_measures[compm_keys[rep_i]]) * new_tempii[rep_i].bpm +# ) + +# for i in range(rep_i + 1, len(compm_keys)): +# nt = new_tempii[i] + +# if nt == None: +# continue + +# m = compare_measures[compm_keys[i]] +# dur = norm_dur(m) * new_tempii[i].bpm + +# if dur != rep_dur: +# measures_are_aligned = False +# break + +# tuplet_id_counter = 0 + +# if measures_are_aligned: +# time_offset = [0] * len(measures) + +# if padding_required: +# for i, mp in enumerate(measures): +# ii = len(mp) +# time_offset[i] = mp[ii - 1].end.t +# while ii < max_length: +# mp.append("pad") +# ii += 1 + +# notes_last_measure_per_staff = {} +# auto_rest_count = 0 + +# notes_within_measure_per_staff = notes_last_measure_per_staff + +# auto_rest_count, current_measure_content = extract_from_measures( +# parts, +# measures, +# 0, +# staves_per_part, +# auto_rest_count, +# notes_within_measure_per_staff, +# ) + +# last_key_sig_per_staff = {} + +# for s, k in current_measure_content.key_sigs_per_staff.items(): +# last_key_sig_per_staff[s] = ( +# min(k, key=lambda k: k.start.t) if len(k) > 0 else None +# ) + +# tuplet_id_counter, notes_last_measure_per_staff = create_measure( +# section, +# 0, +# staves_sorted, +# notes_within_measure_per_staff, +# score_def, +# tuplet_id_counter, +# auto_beaming, +# last_key_sig_per_staff, +# current_measure_content, +# ) + +# for measure_i in range(1, len(measures[0])): +# notes_within_measure_per_staff = notes_last_measure_per_staff + +# auto_rest_count, current_measure_content = extract_from_measures( +# parts, +# measures, +# measure_i, +# staves_per_part, +# auto_rest_count, +# notes_within_measure_per_staff, +# ) + +# score_def = create_score_def(measures, measure_i, parts, section) + +# tuplet_id_counter, notes_last_measure_per_staff = create_measure( +# section, +# measure_i, +# staves_sorted, +# notes_within_measure_per_staff, +# score_def, +# tuplet_id_counter, +# auto_beaming, +# last_key_sig_per_staff, +# current_measure_content, +# ) + +# (etree.ElementTree(mei)).write(file_name + ".mei", pretty_print=True) + +# # post processing step necessary +# # etree won't write <,> and & into an element's text +# with open(file_name + ".mei") as result: +# text = list(result.read()) +# new_text = [] + +# i = 0 +# while i < len(text): +# ch = text[i] +# if ch == "&": +# if text[i + 1 : i + 4] == ["l", "t", ";"]: +# ch = "<" +# i += 4 +# elif text[i + 1 : i + 4] == ["g", "t", ";"]: +# ch = ">" +# i += 4 +# elif text[i + 1 : i + 5] == ["a", "m", "p", ";"]: +# i += 5 +# else: +# i += 1 +# else: +# i += 1 + +# new_text.append(ch) + +# new_text = "".join(new_text) + +# with open(file_name + ".mei", "w") as result: +# result.write(new_text) diff --git a/partitura/io/exportmidi.py b/partitura/io/exportmidi.py index 0de880f1..91e64da3 100644 --- a/partitura/io/exportmidi.py +++ b/partitura/io/exportmidi.py @@ -3,11 +3,17 @@ import numpy as np from collections import defaultdict, OrderedDict +from typing import Union, Optional, Iterable + from mido import MidiFile, MidiTrack, Message, MetaMessage import partitura.score as score +from partitura.score import Score, Part, PartGroup, ScoreLike +from partitura.performance import Performance, PerformedPart, PerformanceLike from partitura.utils import partition +from partitura.utils.misc import deprecated_alias, PathLike + __all__ = ["save_score_midi", "save_performance_midi"] @@ -71,16 +77,21 @@ def get_ppq(parts): return ppq +@deprecated_alias(performed_part="performance_data") def save_performance_midi( - performed_part, out, mpq=500000, ppq=480, default_velocity=64 -): - """Save a :class:`~partitura.performance.PerformedPart` instance as a - MIDI file. + performance_data: PerformanceLike, + out: Optional[PathLike], + mpq: int = 500000, + ppq: int = 480, + default_velocity: int = 64, +) -> Optional[MidiFile]: + """Save a :class:`~partitura.performance.PerformedPart` or + a :class:`~partitura.performance.Performance` as a MIDI file Parameters ---------- - performed_part : :class:`~partitura.performance.PerformedPart` - The performed part to save + performance_data : PerformanceLike + The performance to be saved. out : str or file-like object Either a filename or a file-like object to write the MIDI data to. @@ -94,68 +105,98 @@ def save_performance_midi( A default velocity value (between 0 and 127) to be used for notes without a specified velocity. Defaults to 64. + Returns + ------- + None or MidiFile + If no output is specified using `out`, the function returns + a `MidiFile` object. Otherwise, the function returns None. """ - track_events = defaultdict(lambda: defaultdict(list)) - for c in performed_part.controls: - track = c.get("track", 0) - ch = c.get("channel", 1) - t = int(np.round(10**6 * ppq * c["time"] / mpq)) - track_events[track][t].append( - Message("control_change", control=c["number"], value=c["value"], channel=ch) - ) + if isinstance(performance_data, Performance): + performed_parts = performance_data.performedparts + elif isinstance(performance_data, PerformedPart): + performed_parts = [performance_data] + elif isinstance(performance_data, Iterable): + if not all(isinstance(pp, PerformedPart) for pp in performance_data): + raise ValueError( + "`performance_data` should be a `Performance`, a `PerformedPart`," + " or a list of `PerformedPart` instances" + ) + performed_parts = performed_parts - for n in performed_part.notes: - track = n.get("track", 0) - ch = n.get("channel", 1) - t_on = int(np.round(10**6 * ppq * n["note_on"] / mpq)) - t_off = int(np.round(10**6 * ppq * n["note_off"] / mpq)) - vel = n.get("velocity", default_velocity) - track_events[track][t_on].append( - Message("note_on", note=n["midi_pitch"], velocity=vel, channel=ch) - ) - track_events[track][t_off].append( - Message("note_off", note=n["midi_pitch"], velocity=0, channel=ch) + else: + raise ValueError( + "`performance_data` should be a `Performance`, a `PerformedPart`," + f" or a list of `PerformedPart` instances but is {type(performance_data)}" ) - for p in performed_part.programs: - track = p.get("track", 0) - ch = p.get("channel", 1) - t = int(np.round(10**6 * ppq * p["time"] / mpq)) - track_events[track][t].append( - Message("program_change", program=int(p["program"]), channel=ch) - ) + track_events = defaultdict(lambda: defaultdict(list)) - if len(performed_part.programs) == 0: - # Add default program (to each track/channel) - channels_and_tracks = np.array( - list( - set( - [ - (c.get("channel", 1), c.get("track", 0)) - for c in performed_part.controls - ] - + [ - (n.get("channel", 1), n.get("track", 0)) - for n in performed_part.notes - ] + for performed_part in performed_parts: + for c in performed_part.controls: + track = c.get("track", 0) + ch = c.get("channel", 1) + t = int(np.round(10 ** 6 * ppq * c["time"] / mpq)) + track_events[track][t].append( + Message( + "control_change", + control=c["number"], + value=c["value"], + channel=ch, ) - ), - dtype=int, - ) + ) - timepoints = [] - for tr in track_events.keys(): - timepoints += list(track_events[tr].keys()) - timepoints = list(set(timepoints)) - - for tr in np.unique(channels_and_tracks[:, 1]): - channel_idxs = np.where(channels_and_tracks[:, 1] == tr)[0] - track_channels = np.unique(channels_and_tracks[channel_idxs, 0]) - for ch in track_channels: - track_events[tr][min(timepoints)].append( - Message("program_change", program=0, channel=ch) - ) + for n in performed_part.notes: + track = n.get("track", 0) + ch = n.get("channel", 1) + t_on = int(np.round(10 ** 6 * ppq * n["note_on"] / mpq)) + t_off = int(np.round(10 ** 6 * ppq * n["note_off"] / mpq)) + vel = n.get("velocity", default_velocity) + track_events[track][t_on].append( + Message("note_on", note=n["midi_pitch"], velocity=vel, channel=ch) + ) + track_events[track][t_off].append( + Message("note_off", note=n["midi_pitch"], velocity=0, channel=ch) + ) + + for p in performed_part.programs: + track = p.get("track", 0) + ch = p.get("channel", 1) + t = int(np.round(10 ** 6 * ppq * p["time"] / mpq)) + track_events[track][t].append( + Message("program_change", program=int(p["program"]), channel=ch) + ) + + if len(performed_part.programs) == 0: + # Add default program (to each track/channel) + channels_and_tracks = np.array( + list( + set( + [ + (c.get("channel", 1), c.get("track", 0)) + for c in performed_part.controls + ] + + [ + (n.get("channel", 1), n.get("track", 0)) + for n in performed_part.notes + ] + ) + ), + dtype=int, + ) + + timepoints = [] + for tr in track_events.keys(): + timepoints += list(track_events[tr].keys()) + timepoints = list(set(timepoints)) + + for tr in np.unique(channels_and_tracks[:, 1]): + channel_idxs = np.where(channels_and_tracks[:, 1] == tr)[0] + track_channels = np.unique(channels_and_tracks[channel_idxs, 0]) + for ch in track_channels: + track_events[tr][min(timepoints)].append( + Message("program_change", program=0, channel=ch) + ) midi_type = 0 if len(track_events) == 1 else 1 @@ -173,22 +214,31 @@ def save_performance_midi( track.append(msg.copy(time=t_delta)) t_delta = 0 t = t_msg - if out: + if out is not None: if hasattr(out, "write"): mf.save(file=out) else: mf.save(out) + else: + return mf +@deprecated_alias(parts="score_data") def save_score_midi( - parts, out, part_voice_assign_mode=0, velocity=64, anacrusis_behavior="shift" -): + score_data: ScoreLike, + out: Optional[PathLike], + part_voice_assign_mode: int = 0, + velocity: int = 64, + anacrusis_behavior: str = "shift", +) -> Optional[MidiFile]: """Write data from Part objects to a MIDI file Parameters ---------- - parts : Part, PartGroup or list of these - The musical score to be saved. + score_data : Score, list, Part, or PartGroup + The musical score to be saved. A :class:`partitura.score.Score` object, + a :class:`partitura.score.Part`, a :class:`partitura.score.PartGroup` or + a list of these. out : str or file-like object Either a filename or a file-like object to write the MIDI data to. @@ -228,8 +278,26 @@ def save_score_midi( time points are shifted by the anacrusis (i.e., the first note starts at 0). If "pad_bar", the "incomplete" bar of the anacrusis is padded with silence. Defaults to 'shift'. + + Returns + ------- + None or MidiFile + If no output is specified using `out`, the function returns + a `MidiFile` object. Otherwise, the function returns None. """ + if isinstance(score_data, Score): + parts = score_data.parts + elif isinstance(score_data, (Part, PartGroup)): + parts = [score_data] + elif isinstance(score_data, Iterable): + parts = score_data + + else: + raise ValueError( + "`score_data` should be a `Score`, a `Part`, a `PartGroup" + f" or a list of `Part` instances but is {type(score_data)}" + ) ppq = get_ppq(parts) events = defaultdict(lambda: defaultdict(list)) @@ -353,3 +421,5 @@ def to_ppq(t): mf.save(file=out) else: mf.save(out) + else: + return mf diff --git a/partitura/io/exportmusicxml.py b/partitura/io/exportmusicxml.py index 1011620a..be06abf5 100644 --- a/partitura/io/exportmusicxml.py +++ b/partitura/io/exportmusicxml.py @@ -5,10 +5,13 @@ from lxml import etree import partitura.score as score from operator import itemgetter +from typing import Optional from .importmusicxml import DYN_DIRECTIONS, PEDAL_DIRECTIONS from partitura.utils import partition, iter_current_next, to_quarter_tempo +from partitura.utils.misc import deprecated_alias, PathLike + __all__ = ["save_musicxml"] DOCTYPE = """""" # noqa: E501 @@ -960,14 +963,20 @@ def do_attributes(part, start, end): return result -def save_musicxml(parts, out=None): - """Save a one or more Part or PartGroup instances in MusicXML format. +@deprecated_alias(parts="score_data") +def save_musicxml( + score_data: score.ScoreLike, + out: Optional[PathLike] = None, +) -> Optional[str]: + """ + Save a one or more Part or PartGroup instances in MusicXML format. Parameters ---------- - parts : Score, list, Part, or PartGroup - A :class:`partitura.score.Part` object, - :class:`partitura.score.PartGroup` or a list of these + score_data : Score, list, Part, or PartGroup + The musical score to be saved. A :class:`partitura.score.Score` object, + a :class:`partitura.score.Part`, a :class:`partitura.score.PartGroup` or + a list of these. out: str, file-like object, or None, optional Output file @@ -976,13 +985,12 @@ def save_musicxml(parts, out=None): None or str If no output file is specified using `out` the function returns the MusicXML data as a string. Otherwise the function returns None. - """ - if not isinstance(parts, score.Score): - parts = score.Score( + if not isinstance(score_data, score.Score): + score_data = score.Score( id=None, - partlist=parts, + partlist=score_data, ) root = etree.Element("score-partwise") @@ -1048,8 +1056,7 @@ def handle_parents(part): group_stack.append(pg) - # for part in score.iter_parts(parts): - for part in parts: + for part in score_data: handle_parents(part) diff --git a/partitura/io/exportparangonada.py b/partitura/io/exportparangonada.py index fec1de0d..57ceef69 100644 --- a/partitura/io/exportparangonada.py +++ b/partitura/io/exportparangonada.py @@ -1,9 +1,29 @@ -from partitura.utils import ensure_notearray -import numpy as np +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +This module contains methods for saving Parangonada csv files +""" + import os +import numpy as np + +from typing import Union, List, Iterable, Tuple, Optional + +from partitura.score import ScoreLike +from partitura.performance import PerformanceLike, Performance, PerformedPart + +from partitura.utils import ensure_notearray + +from partitura.utils.misc import PathLike, deprecated_alias + +__all__ = [ + "save_parangonada_csv", + "save_parangonada_alignment", +] -def alignment_dicts_to_array(alignment): + +def alignment_dicts_to_array(alignment: List[dict]) -> np.ndarray: """ create structured array from list of dicts type alignment. @@ -28,40 +48,68 @@ def alignment_dicts_to_array(alignment): # for all dicts create an appropriate entry in an array: # match = 0, deletion = 1, insertion = 2 for no, i in enumerate(alignment): + if i["label"] == "match": array.append((no, "0", i["score_id"], str(i["performance_id"]))) elif i["label"] == "insertion": array.append((no, "2", "undefined", str(i["performance_id"]))) elif i["label"] == "deletion": array.append((no, "1", i["score_id"], "undefined")) + alignarray = np.array(array, dtype=fields) return alignarray -def save_csv_for_parangonada(outdir, part, ppart, align, zalign=None, feature=None): +@deprecated_alias( + spart="score_data", + ppart="performance_data", + align="alignment", +) +def save_parangonada_csv( + alignment: List[dict], + performance_data: Union[PerformanceLike, np.ndarray], + score_data: Union[ScoreLike, np.ndarray], + outdir: Optional[PathLike] = None, + zalign: Optional[List[dict]] = None, + feature: Optional[List[dict]] = None, +) -> Optional[Tuple[np.ndarray]]: """ Save an alignment for visualization with parangonda. Parameters ---------- - outdir : str + alignment : list + A list of note alignment dictionaries. + performance_data : Performance, PerformedPart, structured ndarray + The performance information + score_data : ScoreLike + The musical score. A :class:`partitura.score.Score` object, + a :class:`partitura.score.Part`, a :class:`partitura.score.PartGroup` or + a list of these. + outdir : PathLike A directory to save the files into. - part : Part, structured ndarray - A score part or its note_array. ppart : PerformedPart, structured ndarray A PerformedPart or its note_array. - align : list - A list of note alignment dictionaries. zalign : list, optional A second list of note alignment dictionaries. feature : list, optional A list of expressive feature dictionaries. + Returns + ------- + perf_note_array : np.ndarray + The performance note array. Only returned if `outdir` is None. + score_note_array: np.ndarray + The note array from the score. Only returned if `outdir` is None. + alignarray: np.ndarray + zalignarray: np.ndarray + featurearray: np.ndarray """ - part = ensure_notearray(part) - ppart = ensure_notearray(ppart) + score_note_array = ensure_notearray(score_data) + + perf_note_array = ensure_notearray(performance_data) ffields = [ ("velocity", " None: """ load an alignment exported from parangonda. Parameters ---------- - outfile : str - A path for the alignment tsv file. - ppart : PerformedPart, structured ndarray - A PerformedPart or its note_array. - align : list + alignment : list A list of note alignment dictionaries. - + performance_data : PerformanceLike + A performance. + out : str + A path for the alignment tsv file. """ + if isinstance(performance_data, (Performance, Iterable)): + ppart = performance_data[0] + elif isinstance(performance_data, PerformedPart): + ppart = performance_data notes_indexed_by_id = { str(n["id"]): [ str(n["id"]), @@ -222,7 +279,7 @@ def save_alignment_for_ASAP(outfile, ppart, alignment): ] for n in ppart.notes } - with open(outfile, "w") as f: + with open(out, "w") as f: f.write("xml_id\tmidi_id\ttrack\tchannel\tpitch\tonset\n") for line in alignment: if line["label"] == "match": @@ -236,37 +293,3 @@ def save_alignment_for_ASAP(outfile, ppart, alignment): outline_score = ["insertion"] outline_perf = notes_indexed_by_id[str(line["performance_id"])] f.write("\t".join(outline_score + outline_perf) + "\n") - - -def load_alignment_from_ASAP(outfile): - """ - load a note alignment of the ASAP dataset. - - Parameters - ---------- - outfile : str - A path to the alignment tsv file - - Returns - ------- - alignlist : list - A list of note alignment dictionaries. - """ - alignlist = list() - with open(outfile, "r") as f: - for line in f.readlines(): - fields = line.split("\t") - if fields[0][0] == "n" and "deletion" not in fields[1]: - alignlist.append( - { - "label": "match", - "score_id": fields[0], - "performance_id": fields[1], - } - ) - elif fields[0] == "insertion": - alignlist.append({"label": "insertion", "performance_id": fields[1]}) - elif fields[0][0] == "n" and "deletion" in fields[1]: - alignlist.append({"label": "deletion", "score_id": fields[0]}) - - return alignlist diff --git a/partitura/io/importkern.py b/partitura/io/importkern.py index 03309b67..2c4983a6 100644 --- a/partitura/io/importkern.py +++ b/partitura/io/importkern.py @@ -1,11 +1,15 @@ -import os.path import re import warnings -import partitura.score -import partitura.score as score +from typing import Union, Optional + import numpy as np +import partitura.score as score +from partitura.utils import PathLike, get_document_name +from partitura.utils.misc import deprecated_alias, deprecated_parameter + + __all__ = ["load_kern"] @@ -138,7 +142,7 @@ def _handle_ties(self): dnote = self.nid_dict[self.tie_dict["close"][index]] m_num = [ m - for m in self.part.iter_all(partitura.score.Measure) + for m in self.part.iter_all(score.Measure) if m.start.t == self.part.measure_map(dnote.start.t)[0] ][0].number warnings.warn( @@ -157,7 +161,7 @@ def _handle_ties(self): dnote = self.nid_dict[self.tie_dict["open"][index]] m_num = [ m - for m in self.part.iter_all(partitura.score.Measure) + for m in self.part.iter_all(score.Measure) if m.start.t == self.part.measure_map(dnote.start.t)[0] ][0].number warnings.warn( @@ -173,7 +177,7 @@ def _handle_ties(self): ): self.nid_dict[oid].tie_next = self.nid_dict[cid] self.nid_dict[cid].tie_prev = self.nid_dict[oid] - except: + except Exception: raise ValueError( "Tie Mismatch! Uneven amount of closing to open tie brackets." ) @@ -509,7 +513,7 @@ def _handle_pickup_position(self): def find_lcm(self, doc): kern_string = "-".join([row for row in doc]) - match = re.findall("([0-9]+)([a-g]|[A-G]|r|\.)", kern_string) + match = re.findall(r"([0-9]+)([a-g]|[A-G]|r|\.)", kern_string) durs, _ = zip(*match) x = np.array(list(map(lambda x: int(x), durs))) divs = np.lcm.reduce(np.unique(x)) @@ -517,13 +521,13 @@ def find_lcm(self, doc): # functions to initialize the kern parser -def parse_kern(kern_path): +def parse_kern(kern_path: PathLike) -> np.ndarray: """ Parses an KERN file from path to an regular expression. Parameters ---------- - kern_path : str + kern_path : PathLike The path of the KERN document. Returns ------- @@ -563,28 +567,27 @@ def parse_kern(kern_path): elif i < k: temp.append(i) merge_index = temp - # Final filter for mistabs and inconsistent tabs that would create extra empty voice and would mess the parsing. + # Final filter for mistabs and inconsistent tabs that would create + # extra empty voice and would mess the parsing. striped_parts = [[el for el in part if el != ""] for part in striped_parts] numpy_parts = np.array(list(zip(striped_parts))).squeeze(1).T return numpy_parts -def load_kern(kern_path: str, ensure_list=True, force_note_ids=None, parallel=False): +@deprecated_alias(kern_path="filename") +@deprecated_parameter("ensure_list") +def load_kern( + filename: PathLike, + force_note_ids: Optional[Union[bool, str]] = None, + parallel: bool = False, +) -> score.Score: """Parse a Kern file and build a composite score ontology structure from it (see also scoreontology.py). Parameters ---------- - kern_path : str + filename : PathLike Path to the Kern file to be parsed - ensure_list : bool, optional - When True return a list independent of how many part or - partgroup elements were created from the MIDI file. By - default, when the return value of `load_musicxml` produces a - single : class:`partitura.score.Part` or - :Class:`partitura.score.PartGroup` element, the element itself - is returned instead of a list containing the element. Defaults - to False. force_note_ids : (bool, 'keep') optional. When True each Note in the returned Part(s) will have a newly assigned unique id attribute. Existing note id attributes in @@ -593,13 +596,13 @@ def load_kern(kern_path: str, ensure_list=True, force_note_ids=None, parallel=Fa Returns ------- - partlist : list - A list of either Part or PartGroup objects - + scr: :class:`partitura.score.Score` + A `Score` object """ # parse kern file - numpy_parts = parse_kern(kern_path) - doc_name = os.path.basename(kern_path[:-4]) + numpy_parts = parse_kern(filename) + # doc_name = os.path.basename(filename[:-4]) + doc_name = get_document_name(filename) parser = KernParser(numpy_parts, doc_name) partlist = parser.parts @@ -607,7 +610,7 @@ def load_kern(kern_path: str, ensure_list=True, force_note_ids=None, parallel=Fa partlist, keep=(force_note_ids is True or force_note_ids == "keep") ) - if not ensure_list and len(partlist) == 1: - return partlist[0] - else: - return partlist + # TODO: Parse score info (composer, lyricist, etc.) + scr = score.Score(id=doc_name, partlist=partlist) + + return scr diff --git a/partitura/io/importmatch.py b/partitura/io/importmatch.py index 73435731..c9b32dca 100644 --- a/partitura/io/importmatch.py +++ b/partitura/io/importmatch.py @@ -6,9 +6,9 @@ import re from fractions import Fraction from operator import attrgetter, itemgetter -import logging import warnings +from typing import Tuple, Union, List import numpy as np from scipy.interpolate import interp1d @@ -23,6 +23,12 @@ note_array_from_note_list, ) +from partitura.utils.misc import ( + deprecated_alias, + PathLike, + get_document_name, +) + from partitura.performance import PerformedPart, Performance import partitura.score as score @@ -1368,20 +1374,21 @@ def from_lines(cls, lines, name=""): return matchfile +@deprecated_alias(fn="filename", create_part="create_score") def load_match( - fn, - create_part=False, - pedal_threshold=64, - first_note_at_zero=False, - offset_duration_whole=True, -): + filename: PathLike, + create_score: bool = False, + pedal_threshold: int = 64, + first_note_at_zero: bool = False, + offset_duration_whole: bool = True, +) -> Tuple[Union[Performance, list, score.Score]]: """Load a matchfile. Parameters ---------- - fn : str + filename : str The matchfile - create_part : bool, optional + create_score : bool, optional When True create a Part object from the snote information in the match file. Defaults to False. pedal_threshold : int, optional @@ -1393,38 +1400,46 @@ def load_match( Returns ------- - ppart : list - The performed part, a list of dictionaries + performance : :class:partitura.performance.Performance alignment : list The score--performance alignment, a list of dictionaries - spart : Part - The score part. This item is only returned when `create_part` = True. + scr : :class:partitura.score.Score + The score. This item is only returned when `create_score` = True. """ # Parse Matchfile - mf = MatchFile(fn) + mf = MatchFile(filename) # Generate PerformedPart ppart = performed_part_from_match(mf, pedal_threshold, first_note_at_zero) - performance = Performance(id=fn, performedparts=ppart) + performance = Performance( + id=get_document_name(filename), + performedparts=ppart, + ) # Generate Part - if create_part: + if create_score: if offset_duration_whole: - spart = part_from_matchfile(mf, match_offset_duration_in_whole=True) + spart = part_from_matchfile( + mf, + match_offset_duration_in_whole=True, + ) else: - spart = part_from_matchfile(mf, match_offset_duration_in_whole=False) + spart = part_from_matchfile( + mf, + match_offset_duration_in_whole=False, + ) - scr = score.Score(id=fn, partlist=[spart]) + scr = score.Score(id=get_document_name(filename), partlist=[spart]) # Alignment alignment = alignment_from_matchfile(mf) - if create_part: + if create_score: return performance, alignment, scr else: return performance, alignment -def alignment_from_matchfile(mf): +def alignment_from_matchfile(mf: MatchFile) -> List[dict]: result = [] for line in mf.lines: @@ -1962,7 +1977,7 @@ def performed_part_from_match(mf, pedal_threshold=64, first_note_at_zero=False): first_note = next(mf.iter_notes(), None) if first_note and first_note_at_zero: - offset = first_note.Onset * mpq / (10**6 * ppq) + offset = first_note.Onset * mpq / (10 ** 6 * ppq) else: offset = 0 @@ -1974,9 +1989,9 @@ def performed_part_from_match(mf, pedal_threshold=64, first_note_at_zero=False): dict( id=note.Number, midi_pitch=note.MidiPitch, - note_on=note.Onset * mpq / (10**6 * ppq) - offset, - note_off=note.Offset * mpq / (10**6 * ppq) - offset, - sound_off=sound_off * mpq / (10**6 * ppq) - offset, + note_on=note.Onset * mpq / (10 ** 6 * ppq) - offset, + note_off=note.Offset * mpq / (10 ** 6 * ppq) - offset, + sound_off=sound_off * mpq / (10 ** 6 * ppq) - offset, velocity=note.Velocity, ) ) @@ -1987,7 +2002,7 @@ def performed_part_from_match(mf, pedal_threshold=64, first_note_at_zero=False): sustain_pedal.append( dict( number=64, # type='sustain_pedal', - time=ped.Time * mpq / (10**6 * ppq), + time=ped.Time * mpq / (10 ** 6 * ppq), value=ped.Value, ) ) diff --git a/partitura/io/importmei.py b/partitura/io/importmei.py index 2f98d095..3bffa464 100644 --- a/partitura/io/importmei.py +++ b/partitura/io/importmei.py @@ -7,38 +7,48 @@ SIGN_TO_ALTER, estimate_symbolic_duration, ) +from partitura.utils import PathLike, get_document_name +from partitura.utils.misc import deprecated_alias import re -import logging import warnings import numpy as np -def load_mei(mei_path: str) -> list: +@deprecated_alias(mei_path="filename") +def load_mei(filename: PathLike) -> score.Score: """ Loads a Mei score from path and returns a list of Partitura.Part Parameters ---------- - mei_path : str + filename : PathLike The path to an MEI score. Returns ------- - part_list : list - A list of Partitura Part or GroupPart Objects. + scr: :class:`partitura.score.Score` + A `Score` object """ - parser = MeiParser(mei_path) + parser = MeiParser(filename) + doc_name = get_document_name(filename) # create parts from the specifications in the mei parser.create_parts() # fill parts with the content from the mei parser.fill_parts() - return parser.parts + # TODO: Parse score info (composer, lyricist, etc.) + scr = score.Score( + id=doc_name, + partlist=parser.parts, + ) -class MeiParser: - def __init__(self, mei_path): + return scr + + +class MeiParser(object): + def __init__(self, mei_path: PathLike) -> None: document, ns = self._parse_mei(mei_path) self.document = document self.ns = ns # the namespace in the MEI file @@ -336,7 +346,7 @@ def _find_ppq(self): symbolic_duration = self._get_symbolic_duration(el) intsymdur, dots = self._intsymdur_from_symbolic(symbolic_duration) # double the value if we have dots, to be sure be able to encode that with integers in partitura - durs.append(intsymdur * (2**dots)) + durs.append(intsymdur * (2 ** dots)) # add 4 to be sure to not go under 1 ppq durs.append(4) diff --git a/partitura/io/importmidi.py b/partitura/io/importmidi.py index 62041f13..ea9778b2 100644 --- a/partitura/io/importmidi.py +++ b/partitura/io/importmidi.py @@ -1,8 +1,11 @@ #!/usr/bin/env python -import numpy as np -from collections import defaultdict import warnings +from collections import defaultdict +from typing import Union, Optional +import numpy as np + + import mido import partitura.score as score @@ -12,6 +15,11 @@ key_name_to_fifths_mode, fifths_mode_to_key_name, estimate_clef_properties, + deprecated_alias, + deprecated_parameter, + PathLike, + get_document_name, + ensure_notearray ) import partitura.musicanalysis as analysis @@ -19,12 +27,13 @@ # as key for the dict use channel * 128 (max number of pitches) + pitch -def note_hash(channel, pitch): +def note_hash(channel: int, pitch: int) -> int: """Generate a note hash.""" return channel * 128 + pitch -def midi_to_notearray(fn): +@deprecated_alias(fn="filename") +def midi_to_notearray(filename: PathLike) -> np.ndarray: """Load a MIDI file in a note_array. This function should be used to load MIDI files into an @@ -36,7 +45,7 @@ def midi_to_notearray(fn): Parameters ---------- - fn : str + filename : str Path to MIDI file Returns ------- @@ -44,13 +53,22 @@ def midi_to_notearray(fn): Structured array with onset, duration, pitch, velocity, and ID fields. """ - ppart = load_performance_midi(fn, merge_tracks=True)[0] + perf = load_performance_midi(filename, merge_tracks=True) # set sustain pedal threshold to 128 to disable sustain adjusted offsets - ppart.sustain_pedal_threshold = 128 - return ppart.note_array() + for ppart in perf: + ppart.sustain_pedal_threshold = 128 + + note_array = ensure_notearray(perf) + return note_array -def load_performance_midi(fn, default_bpm=120, merge_tracks=False): + +@deprecated_alias(fn="filename") +def load_performance_midi( + filename: Union[PathLike, mido.MidiFile], + default_bpm: Union[int, float] = 120, + merge_tracks: bool = False, +) -> performance.Performance: """Load a musical performance from a MIDI file. This function should be used for MIDI files that encode @@ -65,7 +83,7 @@ def load_performance_midi(fn, default_bpm=120, merge_tracks=False): Parameters ---------- - fn : str + filename : str Path to MIDI file default_bpm : number, optional Tempo to use wherever the MIDI does not specify a tempo. @@ -78,17 +96,22 @@ def load_performance_midi(fn, default_bpm=120, merge_tracks=False): ------- :class:`partitura.performance.Performance` A Performance instance. + """ + if isinstance(filename, mido.MidiFile): + mid = filename + doc_name = filename.filename + else: + mid = mido.MidiFile(filename) + doc_name = get_document_name(filename) - """ - mid = mido.MidiFile(fn) # parts per quarter ppq = mid.ticks_per_beat # microseconds per quarter - mpq = 60 * (10**6 / default_bpm) + mpq = 60 * (10 ** 6 / default_bpm) # convert MIDI ticks in seconds - time_conversion_factor = mpq / (ppq * 10**6) + time_conversion_factor = mpq / (ppq * 10 ** 6) notes = [] controls = [] @@ -112,7 +135,7 @@ def load_performance_midi(fn, default_bpm=120, merge_tracks=False): mpq = msg.tempo - time_conversion_factor = mpq / (ppq * 10**6) + time_conversion_factor = mpq / (ppq * 10 ** 6) warnings.warn( ( @@ -137,7 +160,12 @@ def load_performance_midi(fn, default_bpm=120, merge_tracks=False): elif msg.type == "program_change": programs.append( - dict(time=t, program=msg.program, track=i, channel=msg.channel) + dict( + time=t, + program=msg.program, + track=i, + channel=msg.channel, + ) ) else: @@ -198,18 +226,24 @@ def load_performance_midi(fn, default_bpm=120, merge_tracks=False): note["id"] = f"n{i}" pp = performance.PerformedPart(notes, controls=controls, programs=programs) - return performance.Performance(fn, pp) + + perf = performance.Performance( + id=doc_name, + performedparts=pp, + ) + return perf +@deprecated_parameter("ensure_list") +@deprecated_alias(fn="filename") def load_score_midi( - fn, - part_voice_assign_mode=0, - ensure_list=False, - quantization_unit=None, - estimate_voice_info=True, - estimate_key=False, - assign_note_ids=True, -): + filename: Union[PathLike, mido.MidiFile], + part_voice_assign_mode: Optional[int] = 0, + quantization_unit: Optional[int] = None, + estimate_voice_info: bool = True, + estimate_key: bool = False, + assign_note_ids: bool = True, +) -> score.Score: """Load a musical score from a MIDI file and return it as a Part instance. @@ -228,8 +262,8 @@ def load_score_midi( Parameters ---------- - fn : str - Path to MIDI file + filename : PathLike or mido.MidiFile + Path to MIDI file or mido.MidiFile object. part_voice_assign_mode : {0, 1, 2, 3, 4, 5}, optional This keyword controls how part and voice information is associated to track and channel information in the MIDI file. @@ -252,13 +286,6 @@ def load_score_midi( 5 Return one Part per combination, without voices Defaults to 0. - ensure_list : bool, optional - When True, return a list independent of how many part or partgroup - elements were created from the MIDI file. By default, when the - return value of `load_score_midi` produces a single - :class:`partitura.score.Part` or :class:`partitura.score.PartGroup` - element, the element itself is returned instead of a list - containing the element. Defaults to False. quantization_unit : integer or None, optional Quantize MIDI times to multiples of this unit. If None, the quantization unit is chosen automatically as the smallest @@ -293,7 +320,14 @@ def load_score_midi( Oxford University Press, New York. """ - mid = mido.MidiFile(fn) + + if isinstance(filename, mido.MidiFile): + mid = filename + doc_name = filename.filename + else: + mid = mido.MidiFile(filename) + doc_name = get_document_name(filename) + divs = mid.ticks_per_beat # these lists will contain information from dedicated tracks for meta @@ -338,7 +372,7 @@ def load_score_midi( if msg.type == "key_signature": key_sigs.append((t, msg.key)) if msg.type == "set_tempo": - global_tempos.append((t, 60 * 10**6 / msg.tempo)) + global_tempos.append((t, 60 * 10 ** 6 / msg.tempo)) else: note_on = msg.type == "note_on" note_off = msg.type == "note_off" @@ -511,10 +545,13 @@ def load_score_midi( for t, qpm in global_tempos: part.add(score.Tempo(qpm, unit="q"), t) - if not ensure_list and len(partlist) == 1: - return partlist[0] - else: - return partlist + # TODO: Add info (composer, etc.) + scr = score.Score( + id=doc_name, + partlist=partlist, + ) + + return scr def make_track_to_part_mapping(tr_ch_keys, group_part_voice_keys): @@ -601,7 +638,7 @@ def create_part( key_sigs, part_id=None, part_name=None, -): +) -> score.Part: warnings.warn("create_part", stacklevel=2) part = score.Part(part_id, part_name=part_name) diff --git a/partitura/io/importmusicxml.py b/partitura/io/importmusicxml.py index 42d68367..df875b40 100644 --- a/partitura/io/importmusicxml.py +++ b/partitura/io/importmusicxml.py @@ -2,13 +2,15 @@ # -*- coding: utf-8 -*- -import warnings import os +import warnings import zipfile +from typing import Union, Optional import numpy as np from lxml import etree + # lxml does XSD validation too but has problems with the MusicXML 3.1 XSD, so we use # the xmlschema package for validating MusicXML against the definition import xmlschema @@ -17,6 +19,7 @@ import partitura.score as score from partitura.score import assign_note_ids from partitura.utils import ensure_notearray +from partitura.utils.misc import deprecated_alias, deprecated_parameter, PathLike __all__ = ["load_musicxml", "musicxml_to_notearray"] @@ -156,7 +159,13 @@ def _parse_partlist(partlist): return structure, part_dict -def load_musicxml(xml, ensure_list=False, validate=False, force_note_ids=None): +@deprecated_alias(xml="filename") +@deprecated_parameter("ensure_list") +def load_musicxml( + filename: PathLike, + validate: bool = False, + force_note_ids: Optional[Union[bool, str]] = None +) -> score.Score: """Parse a MusicXML file and build a composite score ontology structure from it (see also scoreontology.py). @@ -164,14 +173,6 @@ def load_musicxml(xml, ensure_list=False, validate=False, force_note_ids=None): ---------- xml : str or file-like object Path to the MusicXML file to be parsed, or a file-like object - ensure_list : bool, optional - When True return a list independent of how many part or - partgroup elements were created from the MusicXML file. By - default, when the return value of `load_musicxml` produces a - single : class:`partitura.score.Part` or - :Class:`partitura.score.PartGroup` element, the element itself - is returned instead of a list containing the element. Defaults - to False. validate : bool, optional When True the validity of the MusicXML is checked against the MusicXML 3.1 specification before loading the file. An @@ -190,13 +191,17 @@ def load_musicxml(xml, ensure_list=False, validate=False, force_note_ids=None): """ - if type(xml) == str: - if zipfile.is_zipfile(xml): - with zipfile.ZipFile(xml) as zipped_xml: + xml = None + if isinstance(filename, str): + if zipfile.is_zipfile(filename): + with zipfile.ZipFile(filename) as zipped_xml: xml = zipped_xml.open( - os.path.splitext(os.path.basename(xml))[0] + ".xml" + os.path.splitext(os.path.basename(filename))[0] + ".xml" ) + if xml is None: + xml = filename + if validate: validate_musicxml(xml, debug=True) # if xml is a file-like object we need to set the read pointer to the @@ -1470,8 +1475,9 @@ def get_ornaments(e): return [a for a in ornaments if e.find(a) is not None] +@deprecated_alias(fn="filename") def musicxml_to_notearray( - fn, + filename, flatten_parts=True, include_pitch_spelling=False, include_key_signature=False, @@ -1506,15 +1512,18 @@ def musicxml_to_notearray( Returns ------- - score : structured array or list of structured arrays + note_arrays : structured array or list of structured arrays Structured array or list of structured arrays containing score information. """ - parts = load_musicxml(fn, ensure_list=True, force_note_ids="keep") + scr = load_musicxml( + filename=filename, + force_note_ids="keep", + ) note_arrays = [] - for part in score.iter_parts(parts): + for part in scr.parts: # Unfold any repetitions in part unfolded_part = score.unfold_part_maximal(part) # Compute note array diff --git a/partitura/io/importnakamura.py b/partitura/io/importnakamura.py index 2c37d61b..cf3b9fe2 100644 --- a/partitura/io/importnakamura.py +++ b/partitura/io/importnakamura.py @@ -13,14 +13,19 @@ import re import numpy as np +from typing import Union, Tuple + from partitura.utils import note_name_to_midi_pitch from partitura.utils.music import SIGN_TO_ALTER +from partitura.utils.misc import PathLike, deprecated_alias + NAME_PATT = re.compile(r"([A-G]{1})([xb\#]*)(\d+)") -def load_nakamuracorresp(fn): +@deprecated_alias(fn="filename") +def load_nakamuracorresp(filename: PathLike) -> Tuple[Union[np.ndarray, list]]: """Load a corresp file as returned by Nakamura et al.'s MIDI to MIDI alignment. Fields of the file format as specified in [8]_: @@ -28,7 +33,7 @@ def load_nakamuracorresp(fn): Parameters ---------- - fn : str + filename : str The nakamura match.txt-file Returns @@ -53,7 +58,7 @@ def load_nakamuracorresp(fn): ("refPitch", "i"), ("refOnvel", "i"), ] - result = np.loadtxt(fn, dtype=dtype, comments="//") + result = np.loadtxt(filename, dtype=dtype, comments="//") align_valid = result["alignID"] != "*" n_align = sum(align_valid) @@ -78,7 +83,8 @@ def load_nakamuracorresp(fn): return align, ref, alignment -def load_nakamuramatch(fn): +@deprecated_alias(fn="filename") +def load_nakamuramatch(filename: PathLike) -> Tuple[Union[np.ndarray, list]]: """Load a match file as returned by Nakamura et al.'s MIDI to musicxml alignment Fields of the file format as specified in [8]_: @@ -87,7 +93,7 @@ def load_nakamuramatch(fn): Parameters ---------- - fn : str + filename : str The nakamura match.txt-file Returns @@ -137,9 +143,9 @@ def load_nakamuramatch(fn): dtype_missing = [("refOntime", "f"), ("refID", "U256")] pattern = r"//Missing\s(\d+)\t(.+)" # load alignment notes - result = np.loadtxt(fn, dtype=dtype, comments="//") + result = np.loadtxt(filename, dtype=dtype, comments="//") # load missing notes - missing = np.fromregex(fn, pattern, dtype=dtype_missing) + missing = np.fromregex(filename, pattern, dtype=dtype_missing) midi_pitch = np.array( [note_name_to_midi_pitch(n.replace("#", r"\#")) for n in result["alignSitch"]] @@ -199,7 +205,8 @@ def load_nakamuramatch(fn): return align, ref, alignment -def load_nakamuraspr(fn): +@deprecated_alias(fn="filename") +def load_nakamuraspr(filename: PathLike) -> np.ndarray: """Load a spr file as returned by Nakamura et al.'s alignment methods. Fields of the file format as specified in [8]_: @@ -212,7 +219,7 @@ def load_nakamuraspr(fn): Parameters ---------- - fn : str + filename : str The nakamura match.txt-file Returns @@ -249,7 +256,7 @@ def load_nakamuraspr(fn): pattern = r"(\d+)\t(.+)\t(.+)\t(.+)\t(.+)\t(.+)\t(.+)" - result = np.fromregex(fn, pattern, dtype=dtype) + result = np.fromregex(filename, pattern, dtype=dtype) note_array = np.empty(len(result), dtype=note_array_dtype) note_array["id"] = result["ID"] diff --git a/partitura/io/importparangonada.py b/partitura/io/importparangonada.py new file mode 100644 index 00000000..53eede30 --- /dev/null +++ b/partitura/io/importparangonada.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +This module contains methods for parsing Parangonada csv files +""" +import os +import numpy as np + +from typing import List + +from partitura.performance import PerformedPart, Performance +from partitura.utils.misc import PathLike, deprecated_alias + +NOTE_ARRAY_DTYPES = dict( + onset_sec=("onset_sec", "f4"), + duration_sec=("duration_sec", "f4"), + onset_beat=("onset_beat", "f4"), + duration_beat=("duration_beat", "f4"), + onset_quarter=("onset_quarter", "f4"), + duration_quarter=("duration_quarter", "f4"), + onset_div=("onset_div", "i4"), + duration_div=("duration_div", "i4"), + pitch=("pitch", "i4"), + voice=("voice", "i4"), + velocity=("velocity", "i4"), + track=("track", "i4"), + channel=("channel", "i4"), + id=("id", "U256"), +) + +__all__ = [ + "load_parangonada_alignment", + "load_parangonada_csv", +] + + +def _load_csv(filename: PathLike) -> np.ndarray: + """ + Load a CSV file where the headers are one of the note array columns + and return a structured array + + Parameters + ---------- + filename: PathLike + Path of the CSV file + + + Returns + ------- + struct_array: np.ndarray + Structured array + """ + raw_array = np.loadtxt( + fname=filename, + delimiter=",", + comments=None, + dtype=str, + ) + + columns = raw_array[0] + dtypes = [NOTE_ARRAY_DTYPES.get(c, (c, "U256")) for c in columns] + + struct_array = np.empty(len(raw_array) - 1, dtype=dtypes) + for i, (c, dt) in enumerate(zip(columns, dtypes)): + + if dt[-1] == "i4": + # Weird behavior trying to cast 0.0 as an integer + struct_array[c] = raw_array[1:, i].astype(float).astype(int) + else: + struct_array[c] = raw_array[1:, i].astype(dt[-1]) + + return struct_array + + +@deprecated_alias(outfile="filename") +def load_parangonada_alignment(filename) -> List[dict]: + """ + load an alignment exported from parangonda. + + Parameters + ---------- + filename : str + A path to the alignment csv file + + Returns + ------- + alignment : list + A list of note alignment dictionaries. + """ + array = np.loadtxt(filename, dtype=str, delimiter=",") + alignment = list() + # match = 0, deletion = 1, insertion = 2 + for k in range(1, array.shape[0]): + if int(array[k, 1]) == 0: + alignment.append( + { + "label": "match", + "score_id": array[k, 2], + "performance_id": array[k, 3], + } + ) + + elif int(array[k, 1]) == 2: + alignment.append({"label": "insertion", "performance_id": array[k, 3]}) + + elif int(array[k, 1]) == 1: + alignment.append({"label": "deletion", "score_id": array[k, 2]}) + return alignment + + +def load_parangonada_csv(dirname: PathLike, create_score: bool = False) -> np.ndarray: + """ + Load Parangonada Project alignment files + + Parameters + ---------- + dirname : PathLike + Directory with the CSV files in Parangonada + create_score: bool + Create a score. For now it just creats a note array, but the argument + name was chosen to be consistent with `load_match`. + + Returns + ------- + performance : partitura.performance.Performance + The performance in the alignment + alignment : List of dict + The main alignment + zalignment : List of dict + The secondary alignment (for comparing the first one) + feature : np.ndarray + A structured array with note-level feature information + score_note_array + A note array containing note information in the score. Will change to a + score object in a future release! + """ + # Will the names change in the future? + perf_note_array_fn = os.path.join(dirname, "ppart.csv") + score_note_array_fn = os.path.join(dirname, "part.csv") + alignment_fn = os.path.join(dirname, "align.csv") + feature_fn = os.path.join(dirname, "feature.csv") + zalign_fn = os.path.join(dirname, "zalign.csv") + + perf_note_array = _load_csv(perf_note_array_fn) + + performed_part = PerformedPart.from_note_array(perf_note_array) + performance = Performance( + performedparts=performed_part, + id=dirname, + ) + + feature = _load_csv(feature_fn) + alignment = load_parangonada_alignment(alignment_fn) + zalignment = load_parangonada_alignment(zalign_fn) + + if create_score: + # TODO: Generate a Score + score_note_array = _load_csv(score_note_array_fn) + + return ( + performance, + alignment, + zalignment, + feature, + score_note_array, + ) + + else: + return ( + performance, + alignment, + zalignment, + feature, + ) + + +@deprecated_alias(outfile="filename") +def load_alignment_from_ASAP(filename: PathLike) -> List[dict]: + """ + load a note alignment of the ASAP dataset. + + Parameters + ---------- + filename : str + A path to the alignment tsv file + + Returns + ------- + alignment : list + A list of note alignment dictionaries. + """ + alignment = list() + with open(filename, "r") as f: + for line in f.readlines(): + fields = line.split("\t") + if fields[0][0] == "n" and "deletion" not in fields[1]: + alignment.append( + { + "label": "match", + "score_id": fields[0], + "performance_id": fields[1], + } + ) + elif fields[0] == "insertion": + alignment.append({"label": "insertion", "performance_id": fields[1]}) + elif fields[0][0] == "n" and "deletion" in fields[1]: + alignment.append({"label": "deletion", "score_id": fields[0]}) + + return alignment diff --git a/partitura/io/musescore.py b/partitura/io/musescore.py index 4d481fed..36d115a8 100644 --- a/partitura/io/musescore.py +++ b/partitura/io/musescore.py @@ -12,9 +12,18 @@ import subprocess from pathlib import Path from tempfile import NamedTemporaryFile, TemporaryDirectory, gettempdir +from typing import Optional, Union from partitura.io.importmusicxml import load_musicxml from partitura.io.exportmusicxml import save_musicxml +from partitura.score import Score + +from partitura.utils.misc import ( + deprecated_alias, + deprecated_parameter, + PathLike, +) + class MuseScoreNotFoundException(Exception): @@ -58,7 +67,13 @@ def find_musescore3(): return result -def load_via_musescore(fn, ensure_list=False, validate=False, force_note_ids=True): +@deprecated_alias(fn="filename") +@deprecated_parameter("ensure_list") +def load_via_musescore( + filename: PathLike, + validate: bool = False, + force_note_ids: Optional[Union[bool, str]] = True, +) -> Score: """Load a score through through the MuseScore program. This function attempts to load the file in MuseScore, export it as @@ -68,16 +83,8 @@ def load_via_musescore(fn, ensure_list=False, validate=False, force_note_ids=Tru Parameters ---------- - fn : str + filename : str Filename of the score to load - ensure_list : bool, optional - When True return a list independent of how many part or - partgroup elements were created from the MIDI file. By - default, when the return value of `load_musicxml` produces a - single : class:`partitura.score.Part` or - :Class:`partitura.score.PartGroup` element, the element itself - is returned instead of a list containing the element. Defaults - to False. validate : bool, optional When True the validity of the MusicXML generated by MuseScore is checked against the MusicXML 3.1 specification before loading the file. An @@ -104,7 +111,7 @@ def load_via_musescore(fn, ensure_list=False, validate=False, force_note_ids=Tru with NamedTemporaryFile(suffix=".musicxml") as xml_fh: - cmd = [mscore_exec, "-o", xml_fh.name, fn] + cmd = [mscore_exec, "-o", xml_fh.name, filename] try: @@ -125,8 +132,7 @@ def load_via_musescore(fn, ensure_list=False, validate=False, force_note_ids=Tru ) return load_musicxml( - xml_fh.name, - ensure_list=ensure_list, + filename=xml_fh.name, validate=validate, force_note_ids=force_note_ids, ) diff --git a/partitura/musicanalysis/__init__.py b/partitura/musicanalysis/__init__.py index e124d305..4385a324 100644 --- a/partitura/musicanalysis/__init__.py +++ b/partitura/musicanalysis/__init__.py @@ -9,6 +9,7 @@ from .key_identification import estimate_key from .pitch_spelling import estimate_spelling from .tonal_tension import estimate_tonaltension +from .meter import estimate_time from .note_features import ( list_note_feats_functions, make_note_features, @@ -26,6 +27,7 @@ "estimate_key", "estimate_spelling", "estimate_tonaltension", + "estimate_time", "list_note_feats_functions", "make_note_features", "make_rest_features", diff --git a/partitura/musicanalysis/meter.py b/partitura/musicanalysis/meter.py new file mode 100644 index 00000000..ba8d6f86 --- /dev/null +++ b/partitura/musicanalysis/meter.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Meter numerator, Beat, and Tempo estimation. + +Implementation adapted from Jakob Woegerbauer +based on a model published by Simon Dixon.[1] + +References +---------- + +.. [1] Simon Dixon (2001), Automatic extraction of + tempo and beat from expressive performances. + Journal of New Music Research, 30(1):39–58 +""" + +import warnings +import numpy as np +# import scipy.spatial.distance as distance +# from scipy.interpolate import interp1d + +from partitura.utils import get_time_units_from_note_array, ensure_notearray, add_field + + +# Scaling factors +MAX = 9999999999999 +MIN_INTERVAL = 0.01 +MAX_INTERVAL = 2 # in seconds +CLUSTER_WIDTH = 1/12 # in seconds +N_CLUSTERS = 100 +INIT_DURATION = 10 # in seconds +TIMEOUT = 10 # in seconds +TOLERANCE_POST = 0.4 # propotion of beat_interval +TOLERANCE_PRE = 0.2 # proportion of beat_interval +TOLERANCE_INNER = 1/12 +CORRECTION_FACTOR = 1/4 # higher => more correction (speed changes) +MAX_AGENTS = 100 # delete low-scoring agents when there are more than MAX_AGENTS +CHORD_SPREAD_TIME = 1/12 # for onset aggregation + + +class MultipleAgents(): + """ + Class to compute inter onset interval clusters + and to instantiate a number of agents to + approximate beat positions. + """ + def run(self, onsets, salience): + self.clusters = [] + self.agents = [] + onsets = np.array(onsets) + salience = np.array(salience) + self.setup_clusters(onsets) + self.init_tracking(onsets, salience) + self.track(onsets, salience) + + def getTempo(self): + if len(self.agents) == 0: + return 120 + return self.agents[0].getTempo() + + def getNum(self): + if len(self.agents) == 0: + return 4 + return self.agents[0].getTimeSignatureNum() + + def getBeats(self): + if len(self.agents) > 0: + return self.agents[0].history + return [] + + def setup_clusters(self, onsets): + # create inter-onset interval clusters + self.clusters = [] + for i in range(len(onsets)): + for j in range(i+1, len(onsets)): + ioi = onsets[j]-onsets[i] + if ioi < MIN_INTERVAL: + continue + if ioi > MAX_INTERVAL: + break + c_min = False + for c in self.clusters: + k = c.getK(ioi) + if k: + c_min = c + if c_min: + c_min.addIoi(ioi) + else: + self.clusters.append(Cluster(ioi)) + + # merge clusters + i = 0 + while i < len(self.clusters): + c_i = self.clusters[i] + i = i+1 + j = i + while j < len(self.clusters): + if abs(c_i.interval - self.clusters[j].interval) < CLUSTER_WIDTH: + c_i.addIoi(self.clusters[j].iois) + self.clusters.remove(self.clusters[j]) + else: + j += 1 + + # sanitize + for c in self.clusters: + if c.interval <= 0: + continue + while c.interval < MIN_INTERVAL: + c.interval *= 2 + while c.interval > MAX_INTERVAL: + c.interval /= 2 + + # merge again + i = 0 + while i < len(self.clusters): + c_i = self.clusters[i] + i = i+1 + j = i + while j < len(self.clusters): + if abs(c_i.interval - self.clusters[j].interval) < CLUSTER_WIDTH: + c_i.addIoi(self.clusters[j].iois) + self.clusters.remove(self.clusters[j]) + else: + j += 1 + + # calculate cluster scores + for c_i in self.clusters: + for c_j in self.clusters: + n = round(c_j.interval / c_i.interval) + if abs(c_i.interval - n*c_j.interval) < CLUSTER_WIDTH: + c_i.score += Cluster.relationship_factor(n) * len(c_j.iois) + + self.clusters = sorted(self.clusters, key=lambda x: x.score, reverse=True)[ + :N_CLUSTERS] + + def init_tracking(self, onsets, salience): + self.agents = [] + for c in self.clusters: + i = 0 + while i < len(onsets) and onsets[i] < INIT_DURATION: + a = Agent() + a.beat_interval = c.interval + a.history.append((onsets[i], salience[i])) + a.prediction = onsets[i] + c.interval + a.score = salience[i] + self.agents.append(a) + i += 1 + + def track(self, onsets, salience): + for e_i in range(len(onsets)): + e = onsets[e_i] + new_agents = [] + remove_agents = [] + for a in self.agents: + if e - a.lastBeat() > TIMEOUT: + remove_agents.append(a) + else: + while a.prediction + TOLERANCE_POST*a.beat_interval < e: + a.history.append((a.prediction, 0)) + a.prediction += a.beat_interval + if a.prediction - TOLERANCE_PRE*a.beat_interval <= e and e <= a.prediction + TOLERANCE_POST*a.beat_interval: + if abs(a.prediction - e) > TOLERANCE_INNER: + a_new = Agent() + a_new.beat_interval = a.beat_interval + a_new.history = a.history[:] + a_new.prediction = a.prediction + a_new.score = a.score + new_agents.append(a_new) + err = e - a.prediction + a.beat_interval = a.beat_interval + err*CORRECTION_FACTOR + a.prediction = e + a.beat_interval + a.history.append((e, salience[e_i])) + a.score += (1-abs(err/a.beat_interval)/2.) * salience[e_i] + + for a in remove_agents: + self.agents.remove(a) + self.agents = self.agents + new_agents + + # remove duplicate agents + duplicate = np.zeros(len(self.agents)) + agents_all = self.agents[:] + self.agents = [] + for i in range(len(agents_all)): + for j in range(i+1, len(agents_all)): + if duplicate[i] > 0 or duplicate[j] > 0: + continue + + if abs(agents_all[i].beat_interval - agents_all[j].beat_interval) < 0.01 \ + and abs(agents_all[i].lastBeat() - agents_all[j].lastBeat()) < 0.02: + if agents_all[i].score > agents_all[j].score: + duplicate[j] += 1 + else: + duplicate[i] += 1 + break + + self.agents = sorted(np.asarray(agents_all)[(duplicate < 1)].tolist( + ), key=lambda x: x.score, reverse=True)[:MAX_AGENTS] + + self.agents = sorted(self.agents, key=lambda x: x.score, reverse=True) + + +class Cluster(): + """ + Class for inter onset interval clusters. + + Parameters + ---------- + ioi : float + an initial inter onset interval + + """ + + def __init__(self, ioi) -> None: + self.iois = np.zeros(0) + self.score = 0 + self.interval = 0 + self.addIoi(ioi) + + def getK(self, ioi): + diff = abs(self.interval-ioi) + if diff < CLUSTER_WIDTH: + return diff + return False + + def addIoi(self, ioi): + self.iois = np.append(self.iois, ioi) + self.interval = np.sum(self.iois)/len(self.iois) + + @staticmethod + def relationship_factor(d): + if 1 <= d and d <= 4: + return 6-d + elif 5 <= d and d <= 8: + return 1 + return 0 + +class Agent(): + """ + Class for beat induction agents. + """ + + def __init__(self) -> None: + self.beat_interval = 0 + self.prediction = 0 + self.history = [] + self.score = 0 + + def lastBeat(self): + i = len(self.history)-1 + while i > 0 and self.history[i][1] == 0: + i-=1 + return self.history[i][0] + + def getTempo(self): + return 60.0 * (len(self.history)-1) / (self.history[-1][0]-self.history[0][0]) + + def getTimeSignatureNum(self): + possibleNums = [2, 3, 4, 6, 9, 12, 24] + bestVal = {num:0 for num in possibleNums} + salience = list(zip(*self.history))[1] + sumSalience = sum(salience) + f = 1.005 + for num in possibleNums: + for startIdx in range(num): + dbs = len(salience[startIdx::num]) + if dbs > 1: + downbeatSalience = sum(salience[startIdx::num])/dbs + sumSalience = sum(salience[:(dbs-1)*num]) + otherSalience = (sumSalience-downbeatSalience*dbs)/((num-1)*(dbs-1)) + else: + downbeatSalience = 0 + otherSalience = 1 + + ratio = downbeatSalience/otherSalience + bestVal[num] = max(bestVal[num], ratio) + + bestNum = max(bestVal, key=bestVal.get) + + return bestNum + + +def estimate_time(note_info): + """ + Estimate tempo, meter (currently only time signature numerator), and beats + + Parameters + ---------- + note_info : structured array, `Part` or `PerformedPart` + Note information as a `Part` or `PerformedPart` instances or + as a structured array. If it is a structured array, it has to + contain the fields generated by the `note_array` properties + of `Part` or `PerformedPart` objects. If the array contains + onset and duration information of both score and performance, + (e.g., containing both `onset_beat` and `onset_sec`), the score + information will be preferred. + + Returns + ------- + dict + Tempo, meter, and beat information + """ + + note_array = ensure_notearray(note_info) + onset_kw, _ = get_time_units_from_note_array(note_array) + onsets_raw = note_array[onset_kw] + + # aggregate notes in clusters + aggregated_notes = [(0,0)] + for note_on in onsets_raw: + prev_note_on = aggregated_notes[-1][0] + prev_note_salience = aggregated_notes[-1][1] + if abs(note_on - prev_note_on) < CHORD_SPREAD_TIME: + aggregated_notes[-1] = (note_on, prev_note_salience + 1) + else: + aggregated_notes.append((note_on, 1)) + + print(aggregated_notes) + onsets, saliences = list(zip(*aggregated_notes)) + + ma = MultipleAgents() + ma.run(onsets, saliences) + + return dict(tempo=ma.getTempo(), + meter_numerator=ma.getNum(), + beats=ma.getBeats()) + \ No newline at end of file diff --git a/partitura/musicanalysis/note_features.py b/partitura/musicanalysis/note_features.py index 79a8a2b5..bce4be86 100644 --- a/partitura/musicanalysis/note_features.py +++ b/partitura/musicanalysis/note_features.py @@ -398,7 +398,7 @@ def full_note_array(part): def polynomial_pitch_feature(na, part): """Normalize pitch feature.""" - pitches = na["pitch"].astype(np.float) + pitches = na["pitch"].astype(float) feature_names = ["pitch"] max_pitch = 127 W = pitches / max_pitch diff --git a/partitura/musicanalysis/performance_codec.py b/partitura/musicanalysis/performance_codec.py index 100a1e49..64957e1d 100644 --- a/partitura/musicanalysis/performance_codec.py +++ b/partitura/musicanalysis/performance_codec.py @@ -1,6 +1,18 @@ -import numpy -import numpy.lib.recfunctions as rfn import numpy as np +import numpy.lib.recfunctions as rfn +try: + import torch +except ImportError: + # Dummy module to avoid ImportErrors + class DummyTorch(object): + Tensor = np.ndarray + + def __init__(self): + pass + + torch = DummyTorch() + + from partitura.score import Part from partitura.performance import PerformedPart from scipy.interpolate import interp1d @@ -9,13 +21,15 @@ def encode_performance( - part: Part, ppart: PerformedPart, alignment: list, return_u_onset_idx=False + part: Part, + ppart: PerformedPart, + alignment: list, + return_u_onset_idx=False, ): """ Encode expressive parameters from a matched performance - Parameters ---------- part : partitura.Part @@ -30,11 +44,13 @@ def encode_performance( Returns ------- parameters : structured array - A performance array with 4 fields: beat_period, velocity, timing, and rticulation_log. + A performance array with 4 fields: beat_period, velocity, + timing, and rticulation_log. snote_ids : dict A dict of snote_ids corresponding to performance notes. unique_onset_idxs : list (optional) - List of unique onset ids. Returned only when return_u_onset_idx is set to True. + List of unique onset ids. Returned only when return_u_onset_idx + is set to True. """ m_score, snote_ids = to_matched_score(part, ppart, alignment) @@ -217,19 +233,19 @@ def decode_articulation(score_durations, articulation_parameter, beat_period): """ Decode articulation """ - art_ratio = 2**articulation_parameter + art_ratio = 2 ** articulation_parameter dur = art_ratio * score_durations * beat_period return dur def encode_tempo( - score_onsets: numpy.ndarray, - performed_onsets: numpy.ndarray, + score_onsets: np.ndarray, + performed_onsets: np.ndarray, score_durations, performed_durations, return_u_onset_idx: bool = False, -) -> numpy.ndarray: +) -> np.ndarray: """ Compute time-related performance parameters from a performance """ @@ -561,3 +577,42 @@ def monotonize_times(s, deltas=None): idx = np.arange(_s.shape[0]) s_mono = interp1d(idx[mask], _s[mask])(idx[1:-1]) return _s[mask], _deltas[mask] + + +def notewise_to_onsetwise(notewise_inputs, unique_onset_idxs): + """Agregate basis functions per onset""" + if isinstance(notewise_inputs, np.ndarray): + if notewise_inputs.ndim == 1: + shape = len(unique_onset_idxs) + else: + shape = (len(unique_onset_idxs),) + notewise_inputs.shape[1:] + onsetwise_inputs = np.zeros(shape, dtype=notewise_inputs.dtype) + elif isinstance(notewise_inputs, torch.Tensor): + onsetwise_inputs = torch.zeros( + (len(unique_onset_idxs), notewise_inputs.shape[1]), + dtype=notewise_inputs.dtype, + ) + + for i, uix in enumerate(unique_onset_idxs): + try: + onsetwise_inputs[i] = notewise_inputs[uix].mean(0) + except TypeError: + for tn in notewise_inputs.dtype.names: + onsetwise_inputs[i][tn] = notewise_inputs[uix][tn].mean() + return onsetwise_inputs + + +def onsetwise_to_notewise(onsetwise_input, unique_onset_idxs): + """Expand onsetwise predictions for each note""" + n_notes = sum([len(uix) for uix in unique_onset_idxs]) + if isinstance(onsetwise_input, np.ndarray): + if onsetwise_input.ndim == 1: + shape = n_notes + else: + shape = (n_notes,) + onsetwise_input.shape[1:] + notewise_inputs = np.zeros(shape, dtype=onsetwise_input.dtype) + elif isinstance(onsetwise_input, torch.Tensor): + notewise_inputs = torch.zeros(n_notes, dtype=onsetwise_input.dtype) + for i, uix in enumerate(unique_onset_idxs): + notewise_inputs[uix] = onsetwise_input[[i]] + return notewise_inputs diff --git a/partitura/performance.py b/partitura/performance.py index b50f3a4b..c0a6c1a9 100644 --- a/partitura/performance.py +++ b/partitura/performance.py @@ -13,7 +13,10 @@ import numpy as np from partitura.utils import note_array_from_part_list -__all__ = ["PerformedPart"] +__all__ = [ + "PerformedPart", + "Performance", +] class PerformedPart(object): @@ -64,13 +67,13 @@ class PerformedPart(object): def __init__( self, - notes, - id=None, - part_name=None, - controls=None, - programs=None, - sustain_pedal_threshold=64, - ): + notes: List[dict], + id: str = None, + part_name: str = None, + controls: List[dict] = None, + programs: List[dict] = None, + sustain_pedal_threshold: int = 64, + ) -> None: super().__init__() self.id = id self.part_name = part_name @@ -81,7 +84,7 @@ def __init__( self.sustain_pedal_threshold = sustain_pedal_threshold @property - def sustain_pedal_threshold(self): + def sustain_pedal_threshold(self) -> int: """The threshold value (number) above which sustain pedal values are considered to be equivalent to on. For values below the threshold the sustain pedal is treated as off. Defaults to 64. @@ -101,7 +104,7 @@ def sustain_pedal_threshold(self): return self._sustain_pedal_threshold @sustain_pedal_threshold.setter - def sustain_pedal_threshold(self, value): + def sustain_pedal_threshold(self, value: int) -> None: # """ # Set the pedal threshold and update the sound_off # of the notes @@ -111,7 +114,18 @@ def sustain_pedal_threshold(self, value): self.notes, self.controls, self._sustain_pedal_threshold ) - def note_array(self): + @property + def num_tracks(self) -> int: + """Number of tracks""" + return len( + set( + [n.get("track", -1) for n in self.notes] + + [c.get("track", -1) for c in self.controls] + + [p.get("track", -1) for p in self.programs] + ) + ) + + def note_array(self, *args, **kwargs) -> np.ndarray: """Structured array containing performance information. The fields are 'id', 'pitch', 'onset_div', 'duration_div', 'onset_sec', 'duration_sec' and 'velocity'. @@ -146,7 +160,12 @@ def note_array(self): return np.array(note_array, dtype=fields) @classmethod - def from_note_array(cls, note_array, id=None, part_name=None): + def from_note_array( + cls, + note_array: np.ndarray, + id: str = None, + part_name: str = None, + ): """Create an instance of PerformedPart from a note_array. Note that this property does not include non-note information (i.e. controls such as sustain pedal). @@ -184,7 +203,11 @@ def from_note_array(cls, note_array, id=None, part_name=None): return cls(id=id, part_name=part_name, notes=notes, controls=None) -def adjust_offsets_w_sustain(notes, controls, threshold=64): +def adjust_offsets_w_sustain( + notes: List[dict], + controls: List[dict], + threshold=64, +) -> None: # get all note offsets offs = np.fromiter((n["note_off"] for n in notes), dtype=float) first_off = np.min(offs) @@ -268,19 +291,42 @@ class Performance(object): See parameters. """ + id: Optional[str] + title: Optional[str] + subtitle: Optional[str] + lyricist: Optional[str] + copyright: Optional[str] + performedparts: List[PerformedPart] + def __init__( self, - id: str, - performedparts: PerformedPart, + performedparts: Union[PerformedPart, Itertype[PerformedPart]], + id: str = None, performer: Optional[str] = None, title: Optional[str] = None, subtitle: Optional[str] = None, composer: Optional[str] = None, lyricist: Optional[str] = None, copyright: Optional[str] = None, + ensure_unique_tracks: bool = True, ) -> None: self.id = id - self.performedparts = [performedparts] + + if isinstance(performedparts, PerformedPart): + self.performedparts = [performedparts] + elif isinstance(performedparts, Itertype): + + if not all([isinstance(pp, PerformedPart) for pp in performedparts]): + raise ValueError( + "`performedparts` should be a list of `PerformedPart` objects!" + ) + self.performedparts = list(performedparts) + else: + raise ValueError( + "`performedparts` should be a `PerformedPart` or a list of " + f"`PerformedPart` objects but is {type(performedparts)}." + ) + # Metadata self.performer = performer self.title = title @@ -289,6 +335,68 @@ def __init__( self.lyricist = lyricist self.copyright = copyright + if ensure_unique_tracks: + self.sanitize_track_numbers() + + @property + def num_tracks(self) -> int: + """ + Number of tracks in the performance + """ + n_tracks = len( + set( + [(i, n.get("track", -1)) for i, pp in enumerate(self) for n in pp.notes] + + [ + (i, c.get("track", -1)) + for i, pp in enumerate(self) + for c in pp.controls + ] + + [ + (i, p.get("track", -1)) + for i, pp in enumerate(self) + for p in pp.programs + ] + ) + ) + + return n_tracks + + def sanitize_track_numbers(self) -> None: + """ + Ensure that the track number info in each `PerformedPart` in + self.performedparts is unique (i.e., that a track number does not appear + in multiple `PerformedPart` instances) + """ + unique_track_ids = list( + set( + [(i, n.get("track", -1)) for i, pp in enumerate(self) for n in pp.notes] + + [ + (i, c.get("track", -1)) + for i, pp in enumerate(self) + for c in pp.controls + ] + + [ + (i, p.get("track", -1)) + for i, pp in enumerate(self) + for p in pp.programs + ] + ) + ) + + track_map = dict([(tid, ti) for ti, tid in enumerate(unique_track_ids)]) + + for i, ppart in enumerate(self): + + for note in ppart.notes: + + note["track"] = track_map[(i, note.get("track", -1))] + + for control in ppart.controls: + control["track"] = track_map[(i, control.get("track", -1))] + + for program in ppart.programs: + program["track"] = track_map[(i, program.get("track", -1))] + def __getitem__(self, index: int) -> PerformedPart: """Get `Part in the score by index""" return self.performedparts[index] @@ -315,9 +423,13 @@ def __len__(self) -> int: """ return len(self.performedparts) - def note_array(self) -> np.ndarray: + def note_array(self, *args, **kwargs) -> np.ndarray: """ Get a note array that concatenates the note arrays of all Part/PartGroup objects in the score. """ - return note_array_from_part_list(part_list=self.performedparts) + return note_array_from_part_list(self.performedparts, *args, **kwargs) + + +# Alias for typing performance-like objects +PerformanceLike = Union[List[PerformedPart], PerformedPart, Performance] diff --git a/partitura/score.py b/partitura/score.py index 04ffc67b..9b3bb49c 100644 --- a/partitura/score.py +++ b/partitura/score.py @@ -997,16 +997,7 @@ def first_point(self): """ return self._points[0] if len(self._points) > 0 else None - def note_array( - self, - include_pitch_spelling=False, - include_key_signature=False, - include_time_signature=False, - include_metrical_position=False, - include_grace_notes=False, - include_staff=False, - include_divs_per_quarter=False, - ): + def note_array(self, **kwargs): """ Create a structured array with note information from a `Part` object. @@ -1045,16 +1036,7 @@ def note_array( note_array : structured array """ - return note_array_from_part( - self, - include_pitch_spelling=include_pitch_spelling, - include_key_signature=include_key_signature, - include_time_signature=include_time_signature, - include_metrical_position=include_metrical_position, - include_grace_notes=include_grace_notes, - include_staff=include_staff, - include_divs_per_quarter=include_divs_per_quarter, - ) + return note_array_from_part(self, **kwargs) def rest_array( self, @@ -2514,7 +2496,7 @@ def microseconds_per_quarter(self): """ return int( - np.round(60 * (10**6 / to_quarter_tempo(self.unit or "q", self.bpm))) + np.round(60 * (10 ** 6 / to_quarter_tempo(self.unit or "q", self.bpm))) ) def __str__(self): @@ -2869,6 +2851,7 @@ class Score(object): See parameters. copyright: str. See parameters. + """ id: Optional[str] @@ -2882,8 +2865,8 @@ class Score(object): def __init__( self, - id: str, partlist: Union[Part, PartGroup, Itertype[Union[Part, PartGroup]]], + id: Optional[str] = None, title: Optional[str] = None, subtitle: Optional[str] = None, composer: Optional[str] = None, @@ -2909,7 +2892,8 @@ def __init__( self.part_structure = list(partlist) else: raise ValueError( - "`partlist` should be a list, a `Part` or a `PartGrop` but is {type(partlist)}" + "`partlist` should be a list, a `Part` or a `PartGrop` but" + f" is {type(partlist)}." ) def __getitem__(self, index: int) -> Part: @@ -2948,6 +2932,7 @@ def note_array( include_grace_notes=False, include_staff=False, include_divs_per_quarter=False, + **kwargs, ) -> np.ndarray: """ Get a note array that concatenates the note arrays of all Part/PartGroup @@ -2962,9 +2947,14 @@ def note_array( include_grace_notes=include_grace_notes, include_staff=include_staff, include_divs_per_quarter=include_divs_per_quarter, + **kwargs, ) +# Alias for typing score-like objects +ScoreLike = Union[List[Union[Part, PartGroup]], Part, PartGroup, Score] + + class ScoreVariant(object): # non-public @@ -3252,9 +3242,15 @@ def iter_parts(partlist): """ if not isinstance(partlist, (list, tuple, set)): - partlist = [partlist] + _partlist = [partlist] + + elif isinstance(partlist, Score): + _partlist = partlist.parts - for el in partlist: + else: + _partlist = partlist + + for el in _partlist: if isinstance(el, Part): yield el else: @@ -3721,8 +3717,8 @@ class Segment(TimedObject): Class that represents any segment between two navigation markers such as repetitions, Volta brackets, or capo/fine/coda/segno directions. - Parameters + ---------- id: string unique, ordererd identifier string to: list @@ -3730,11 +3726,10 @@ class Segment(TimedObject): await_to: list of ids of possible destinations after a jump type : string, optional - String for the type of the segment (either "default" or "leap_start" and "leap_end") - A "leap" tuple has the effect of forcing the fastest (shortest) repetition unfolding after this segment, - as is commonly expected after capo/fine/coda/segno directions. + String for the type of the segment (either "default" or "leap_start" and "leap_end"). A "leap" tuple has the effect of forcing the fastest (shortest) repetition unfolding after this segment, as is commonly expected after capo/fine/coda/segno directions. info: string, optional String to describe the segment, used only for printing (pretty_segments) + """ def __init__(self, id, to, await_to, force_seq=False, type="default", info=""): @@ -4030,6 +4025,7 @@ def get_segments(part): ------- segments: dict A dictionary of Segment objects indexed by segment IDs. + """ return {seg.id: seg for seg in part.iter_all(Segment)} @@ -4548,6 +4544,7 @@ def merge_parts(parts, reassign="voice"): ------- Part A new part that contains the elements of the old parts + """ # check if reassign has valid values if reassign not in ["staff", "voice"]: @@ -4632,7 +4629,8 @@ def merge_parts(parts, reassign="voice"): for p_ind, p in enumerate(parts): for e in p.iter_all(): # full copy the first part and partially copy the others - # we don't copy elements like duplicate barlines, clefs or time signatures for others + # we don't copy elements like duplicate barlines, clefs or + # time signatures for others # TODO : check DaCapo, Fine, Fermata, Ending, Tempo if p_ind == 0 or not isinstance( e, @@ -4641,7 +4639,7 @@ def merge_parts(parts, reassign="voice"): new_start = e.start.t * time_multiplier_per_part[p_ind] new_end = ( e.end.t * time_multiplier_per_part[p_ind] - if not e.end is None + if e.end is not None else None ) if reassign == "voice": diff --git a/partitura/utils/__init__.py b/partitura/utils/__init__.py index 22284716..2e8edd76 100644 --- a/partitura/utils/__init__.py +++ b/partitura/utils/__init__.py @@ -47,6 +47,16 @@ ensure_rest_array, rest_array_from_part_list, ) +from partitura.utils.synth import ( + synthesize +) + +from .misc import ( + PathLike, + get_document_name, + deprecated_alias, + deprecated_parameter, +) __all__ = [ diff --git a/partitura/utils/misc.py b/partitura/utils/misc.py new file mode 100644 index 00000000..9819f77e --- /dev/null +++ b/partitura/utils/misc.py @@ -0,0 +1,150 @@ +import functools +import os +import warnings + +from typing import Union, Callable, Dict, Any, Iterable + +# Recommended by PEP 519 +PathLike = Union[str, bytes, os.PathLike] + + +def get_document_name(filename: PathLike) -> str: + """ + Get the name of a document. + + Parameters + ---------- + filename : PathLike + The path of the file + + Returns + ------- + doc_name : str + The name of the document + """ + doc_name = str(os.path.basename(os.path.splitext(filename)[0])) + return doc_name + + +def deprecated_alias(**aliases: str) -> Callable: + """ + Decorator for aliasing deprecated function and method arguments. + + Use as follows: + + @deprecated_alias(old_arg='new_arg') + def myfunc(new_arg): + ... + + Notes + ----- + Taken from https://stackoverflow.com/a/49802489 by user user2357112. + This code is re-distributed as (Licence) + """ + + def deco(f: Callable): + @functools.wraps(f) + def wrapper(*args, **kwargs): + rename_kwargs(f.__name__, kwargs, aliases) + return f(*args, **kwargs) + + return wrapper + + return deco + + +def deprecated_parameter(*deprecated_kwargs: str) -> Callable: + """ + Decorator for deprecating function and method arguments. + + Use as follows: + + @deprecated_parameter("old_argument1", "old_argument2") + def func(new_arg): + ... + """ + + def deco(f: Callable): + @functools.wraps(f) + def wrapper(*args, **kwargs): + to_be_deprecated(f.__name__, kwargs, deprecated_kwargs) + return f(*args, **kwargs) + + return wrapper + + return deco + + +def rename_kwargs( + func_name: str, + kwargs: Dict[str, Any], + aliases: Dict[str, str], +) -> None: + """ + Helper function for renaming deprecated function arguments. + This function edits the dictionary of keyword arguments in-place. + + Parameters + ---------- + func_name : str + Name of the function which keyword arguments have been deprecated. + kwargs : dictionary + Dictionary of keyword arguments to be passed to the function + aliases: dictionary + Dictionary specifying the aliases of the deprecated keyword arguments. + + + Notes + ----- + Taken from https://stackoverflow.com/a/49802489 by user user2357112. + """ + for alias, new in aliases.items(): + if alias in kwargs: + if new in kwargs: + raise TypeError( + f"{func_name} received both {alias} and {new} as arguments!" + f" {alias} is deprecated, use {new} instead." + ) + warnings.warn( + message=( + f"`{alias}` is deprecated as an argument to `{func_name}`; use" + f" `{new}` instead." + ), + category=DeprecationWarning, + stacklevel=3, + ) + kwargs[new] = kwargs.pop(alias) + + +def to_be_deprecated( + func_name: str, + kwargs: Dict[str, Any], + deprecated_kwargs: Iterable[str], +) -> None: + """ + Helper function for deprecating function arguments. + This function edits the dictionary of keyword arguments in-place. + + Parameters + ---------- + func_name : str + Name of the function which keyword arguments have been deprecated. + kwargs : dictionary + Dictionary of keyword arguments to be passed to the function + deprecated_kwargs: Iterable[str] + An iterable specifiying the parameters to be deprecated. + """ + + for deprecated_kwarg in deprecated_kwargs: + if deprecated_kwarg in kwargs: + # raise warning + warnings.warn( + message=( + f"`{deprecated_kwarg}` is a deprecatd argument of `{func_name}`" + " and will be ignored." + ), + category=DeprecationWarning, + stacklevel=3, + ) + # Remove deprecated kwarg from kwargs + kwargs.pop(deprecated_kwarg) diff --git a/partitura/utils/music.py b/partitura/utils/music.py index 8d075e92..31733263 100644 --- a/partitura/utils/music.py +++ b/partitura/utils/music.py @@ -5,6 +5,7 @@ import numpy as np from scipy.interpolate import interp1d from scipy.sparse import csc_matrix +from typing import Union from partitura.utils.generic import find_nearest, search, iter_current_next MIDI_BASE_CLASS = {"c": 0, "d": 2, "e": 4, "f": 5, "g": 7, "a": 9, "b": 11} @@ -237,6 +238,9 @@ MUSICAL_BEATS = {6: 2, 9: 3, 12: 4} +# Standard tuning frequency of A4 in Hz +A4 = 440.0 + def ensure_notearray(notearray_or_part, *args, **kwargs): """ @@ -246,6 +250,8 @@ def ensure_notearray(notearray_or_part, *args, **kwargs): ---------- notearray_or_part : structured ndarray, `Score`, `Part`, `PerformedPart` Input score information + kwargs : dict + Additional arguments to be passed to `partitura.utils.note_array_from_part()`. Returns ------- @@ -253,7 +259,7 @@ def ensure_notearray(notearray_or_part, *args, **kwargs): Structured array containing score information. """ from partitura.score import Part, PartGroup, Score - from partitura.performance import PerformedPart + from partitura.performance import PerformedPart, Performance if isinstance(notearray_or_part, np.ndarray): if notearray_or_part.dtype.fields is not None: @@ -270,10 +276,10 @@ def ensure_notearray(notearray_or_part, *args, **kwargs): elif isinstance(notearray_or_part, Score): return note_array_from_part_list(notearray_or_part.parts, *args, **kwargs) - elif isinstance(notearray_or_part, PerformedPart): - return notearray_or_part.note_array() + elif isinstance(notearray_or_part, (PerformedPart, Performance)): + return notearray_or_part.note_array(*args, **kwargs) elif isinstance(notearray_or_part, Score): - return notearray_or_part.note_array() + return notearray_or_part.note_array(*args, **kwargs) elif isinstance(notearray_or_part, list): if all([isinstance(part, Part) for part in notearray_or_part]): return note_array_from_part_list(notearray_or_part, *args, **kwargs) @@ -406,6 +412,55 @@ def pitch_spelling_to_note_name(step, alter, octave): return note_name +def midi_pitch_to_frequency( + midi_pitch: Union[int, float, np.ndarray], a4: Union[int, float] = A4 +) -> Union[float, np.ndarray]: + """ + Convert MIDI pitch to frequency in Hz. This method assumes equal temperament. + + Parameters + ---------- + midi_pitch: int, float or ndarray + MIDI pitch of the note(s). + a4 : int or float (optional) + Frequency of A4 in Hz. By default is 440 Hz. + + Returns + ------- + freq : float or ndarray + Frequency of the note(s). + """ + freq = (a4 / 32) * (2 ** ((midi_pitch - 9) / 12)) + return freq + + +def frequency_to_midi_pitch( + freq: Union[int, float, np.ndarray], + a4: Union[int, float] = A4, +) -> Union[int, np.ndarray]: + """ + Convert frequency to MIDI pitch. This method assumes equal temperament. + + Parameters + ---------- + freq : float, int or np.ndarray + Frequency of the note(s) in Hz. + a4 : int or float (optional) + Frequency of A4 in Hz. By default is 440 Hz. + + Returns + ------- + midi_pitch : int or np.ndarray + MIDI pitch of the notes. + """ + midi_pitch = np.round(12 * np.log2(32 * freq / a4) + 9) + + if isinstance(midi_pitch, (int, float)): + return int(midi_pitch) + elif isinstance(midi_pitch, np.ndarray): + return midi_pitch.astype(int) + + SIGN_TO_ALTER = { "n": 0, "#": 1, @@ -1511,12 +1566,7 @@ def remove_silence_from_performed_part(ppart): def note_array_from_part_list( part_list, unique_id_per_part=True, - include_pitch_spelling=False, - include_key_signature=False, - include_time_signature=False, - include_grace_notes=False, - include_staff=False, - include_divs_per_quarter=False, + **kwargs, ): """ Construct a structured Note array from a list of Part objects @@ -1528,28 +1578,9 @@ def note_array_from_part_list( the list must be of the same type (i.e., no mixing `Part` and `PerformedPart` objects in the same list. unique_id_per_part : bool (optional) - Indicate from which part do each note come from in the note ids. - include_pitch_spelling: bool (optional) - Include pitch spelling information in note array. Only valid - if parts in `part_list` are `Part` objects. See `note_array_from_part` - for more info. Default is False. - include_key_signature: bool (optional) - Include key signature information in output note array. - Only valid if parts in `part_list` are `Part` objects. - See `note_array_from_part` for more info. Default is False. - include_time_signature : bool (optional) - Include time signature information in output note array. - Only valid if parts in `part_list` are `Part` objects. - See `note_array_from_part` for more info. Default is False. - include_grace_notes : bool (optional) - If `True`, includes grace note information, i.e. if a note is a - grace note and the grace type "" for non grace notes). - Default is False - include_staff : bool (optional) - If `True`, includes note staff number. - Default is False - include_divs_per_quarter : bool(optional) - If `True`, inclused the number of divs per quarter note. + Indicate from which part do each note come from in the note ids. Default is True. + **kwargs : dict + Additional keyword arguments to pass to `utils.music.note_array_from_part()` Returns ------- @@ -1566,28 +1597,19 @@ def note_array_from_part_list( note_array = [] for i, part in enumerate(part_list): if isinstance(part, (Part, PartGroup)): + # set include_divs_per_quarter, to correctly merge different divs + kwargs["include_divs_per_quarter"] = True is_score = True if isinstance(part, Part): na = note_array_from_part( - part=part, - unique_id_per_part=unique_id_per_part, - include_pitch_spelling=include_pitch_spelling, - include_key_signature=include_key_signature, - include_time_signature=include_time_signature, - include_grace_notes=include_grace_notes, - include_staff=include_staff, - include_divs_per_quarter=True, # necessary for correctly merging + part, + **kwargs ) elif isinstance(part, PartGroup): na = note_array_from_part_list( - part_list=part.children, + part.children, unique_id_per_part=unique_id_per_part, - include_pitch_spelling=include_pitch_spelling, - include_key_signature=include_key_signature, - include_time_signature=include_time_signature, - include_grace_notes=include_grace_notes, - include_staff=include_staff, - include_divs_per_quarter=True, # necessary for correctly merging + **kwargs ) elif isinstance(part, PerformedPart): na = part.note_array() @@ -1803,7 +1825,6 @@ def slice_notearray_by_time( def note_array_from_part( part, - unique_id_per_part=False, include_pitch_spelling=False, include_key_signature=False, include_time_signature=False, @@ -1971,7 +1992,6 @@ def note_array_from_part( def rest_array_from_part( part, - unique_id_per_part=False, include_pitch_spelling=False, include_key_signature=False, include_time_signature=False, diff --git a/partitura/utils/synth.py b/partitura/utils/synth.py new file mode 100644 index 00000000..ae264d22 --- /dev/null +++ b/partitura/utils/synth.py @@ -0,0 +1,382 @@ +""" +Synthesize Partitura Part or Note array to wav using additive synthesis + +TODO +* Add other tuning systems? + +""" +from typing import Union, Tuple, Dict, Optional, Any, Callable + +import numpy as np + +from scipy.interpolate import interp1d + + +from partitura.utils.music import ( + A4, + ensure_notearray, + get_time_units_from_note_array, + midi_pitch_to_frequency, +) + +TWO_PI = 2 * np.pi +SAMPLE_RATE = 44100 +DTYPE = float + +NATURAL_INTERVAL_RATIOS = { + 0: 1, + 1: 16 / 15, # 15/14, 11/10 + 2: 8 / 7, # 9/8, 10/9, 12/11, 13/14 + 3: 6 / 5, # 7/6, + 4: 5 / 4, + 5: 4 / 3, + 6: 7 / 5, # 13/9, + 7: 3 / 2, + 8: 8 / 5, + 9: 5 / 3, + 10: 7 / 4, # 13/7 + 11: 15 / 8, + 12: 2, +} + + +def midi_pitch_to_natural_frequency( + midi_pitch: Union[int, float, np.ndarray], + a4: Union[int, float] = A4, + natural_interval_ratios: Dict[int, float] = NATURAL_INTERVAL_RATIOS, +) -> Union[float, np.ndarray]: + """ + Convert MIDI pitch to frequency in Hz using natural tunning (i.e., with + respect to the harmonic series). + This method computes intervals with respect to A4. + + Parameters + ---------- + midi_pitch: int, float or ndarray + MIDI pitch of the note(s). + a4 : int or float (optional) + Frequency of A4 in Hz. By default is 440 Hz. + + Returns + ------- + freq : float or ndarray + Frequency of the note(s). + + Notes + ----- + This implementation computes the natural interval ratios + (with respect to the harmonic series), but with respect to + octaves centered on A. All intervals are computed with respect + to the A in the same octave as the note in question (e.g., + C4 is a descending major sixth with respect to A4, E5 is descending + perfect fourth computed with respect to A5, etc.). + + + TODO + ---- + * compute intervals with given reference pitch. + """ + + octave = (midi_pitch // 12) - 1 + + aref = 69.0 - 12.0 * (4 - octave) + + aref_freq = a4 / (2.0 ** ((4 - octave))) + + interval = midi_pitch - aref + + if isinstance(interval, (int, float)): + interval = np.array([interval], dtype=int) + + ratios = np.array( + [ + natural_interval_ratios[abs(itv)] ** (1 if itv >= 0 else -1) + for itv in interval + ] + ) + + freqs = aref_freq * ratios + + if isinstance(midi_pitch, (int, float)): + freqs = float(freqs) + return freqs + + +def exp_in_exp_out( + num_frames: int, + dtype: type = DTYPE, +) -> np.ndarray: + """ + Sound envelope with exponential attack and decay + + Parameters + ---------- + num_frames : int + Size of the window in frames. + + Returns + ------- + envelope : np.ndarray + 1D array with the envelope. + """ + # Initialize envelope + envelope = np.ones(num_frames, dtype=dtype) + # number of frames for decay + decay_frames = np.minimum(num_frames // 10, 1000) + # number of frames for attack + attack_frames = np.minimum(num_frames // 100, 1000) + # Compute envelope + envelope[-decay_frames:] = np.exp(-np.linspace(0, 100, decay_frames)).astype(dtype) + envelope[:attack_frames] = np.exp(np.linspace(-100, 0, attack_frames)).astype(dtype) + + return envelope + + +def lin_in_lin_out(num_frames: int, dtype: type = DTYPE) -> np.ndarray: + """ + Sound envelope with linear attack and decay + + Parameters + ---------- + num_frames : int + Size of the window in frames. + + Returns + ------- + envelope : np.ndarray + 1D array with the envelope. + """ + # Initialize envelope + envelope = np.ones(num_frames, dtype=dtype) + # Number of frames for decay + decay_frames = np.minimum(num_frames // 10, 1000) + # number of frames for attack + attack_frames = np.minimum(num_frames // 100, 1000) + # Compute envelope + envelope[-decay_frames:] = np.linspace(1, 0, decay_frames, dtype=dtype) + envelope[:attack_frames] = np.linspace(0, 1, attack_frames, dtype=dtype) + return envelope + + +def additive_synthesis( + freqs: Union[int, float, np.ndarray], + duration: float, + samplerate: Union[int, float] = SAMPLE_RATE, + weights: Union[int, float, str, np.ndarray] = "equal", + envelope_fun="linear", +) -> np.ndarray: + """ + Additive synthesis for a single note + + Parameters + ---------- + freqs: Union[int, float, np.ndarray] + Frequencies of the spectrum of the note. + duration: float + Duration of the note in seconds. + samplerate: int, float + Sample rate of the note. + + """ + if isinstance(freqs, (int, float)): + freqs = [freqs] + + if isinstance(weights, (int, float)): + weights = [weights] + + elif weights == "equal": + weights = np.ones(len(freqs), dtype=DTYPE) / len(freqs) + + freqs = np.array(freqs).reshape(-1, 1) + weights = np.array(weights).reshape(-1, 1) + + if envelope_fun == "linear": + envelope_fun = lin_in_lin_out + elif envelope_fun == "exp": + envelope_fun = exp_in_exp_out + else: + if not callable(envelope_fun): + raise ValueError('`envelope_fun` must be "linear", "exp" or a callable') + + num_frames = int(np.round(duration * samplerate)) + envelope = envelope_fun(num_frames) + x = np.linspace(0, duration, num=num_frames) + output = weights * np.sin(TWO_PI * freqs * x) + + return output.sum(0) * envelope + + +class DistributedHarmonics(object): + def __init__( + self, + n_harmonics: int, + weights: Union[np.ndarray, str] = "equal", + ) -> None: + + self.n_harmonics = n_harmonics + self.weights = weights + + if self.weights == "equal": + self.weights = 1.0 / (self.n_harmonics + 1) * np.ones(self.n_harmonics + 1) + + self._overtones = np.arange(1, self.n_harmonics + 2) + + def __call__(self, freq: float) -> Tuple[np.ndarray]: + + return self._overtones * freq, self.weights + + +class ShepardTones(object): + """ + Generate Shepard Tones + """ + + def __init__( + self, + min_freq: Union[float, int] = 77.8, + max_freq: Union[float, int] = 2349, + ) -> None: + + self.min_freq = min_freq + self.max_freq = max_freq + + x_freq = np.linspace(self.min_freq, self.max_freq, 1000) + + weights = np.hanning(len(x_freq) + 2) + 0.001 + weights /= max(weights) + + self.shepard_weights_fun = interp1d( + x=x_freq, + y=weights[1:-1], + bounds_error=False, + fill_value=weights.min(), + ) + + def __call__(self, freq) -> Tuple[np.ndarray]: + + min_freq = self.min_f(freq) + + freqs = 2 ** np.arange(5) * min_freq + + return freqs, self.shepard_weights_fun(freqs) + + def min_f(self, freq: Union[float, np.ndarray]) -> Union[float, np.ndarray]: + n = np.floor(np.log2(freq) - np.log2(self.min_freq)) + return freq / (2 ** n) + + def max_f(self, freq: Union[float, np.ndarray]) -> Union[float, np.ndarray]: + n = np.floor(np.log2(self.max_freq) - np.log2(freq)) + return freq * (2 ** n) + + +def synthesize( + note_info, + samplerate: int = SAMPLE_RATE, + envelope_fun: str = "linear", + tuning: str = "equal_temperament", + tuning_kwargs: Dict[str, Any] = {"a4": A4}, + harmonic_dist: Optional[Union[str, int]] = None, + bpm: Union[float, int] = 60, +) -> np.ndarray: + """ + Synthesize a partitura object with note information + using additive synthesis + + + Parameters + ---------- + note_info : ScoreLike, PerformanceLike or np.ndarray + A partitura object with note information. + samplerate: int + The sample rate of the audio file in Hz. + envelope_fun: {"linear", "exp" } + The type of envelop to apply to the individual sine waves. + tuning: {"equal_temperament", "natural"} + harmonic_dist : int, "shepard" or None (optional) + Distribution of harmonics. If an integer, it is the number + of harmonics to be considered. If "shepard", it uses Shepard tones. + Default is None (i.e., only consider the fundamental frequency) + bpm : int + The bpm to render the output (if the input is a score-like object) + + Returns + ------- + audio_signal : np.ndarray + Audio signal as a 1D array. + """ + + note_array = ensure_notearray(note_info) + + onset_unit, duration_unit = get_time_units_from_note_array(note_array) + if np.min(note_array[onset_unit]) <= 0: + note_array[onset_unit] = note_array[onset_unit] + np.min(note_array[onset_unit]) + + # If the input is a score, convert score time to seconds + if onset_unit != "onset_sec": + beat2sec = 60 / bpm + onsets = note_array[onset_unit] * beat2sec + offsets = (note_array[onset_unit] + note_array[duration_unit]) * beat2sec + duration = note_array[duration_unit] * beat2sec + else: + onsets = note_array["onset_sec"] + offsets = note_array["onset_sec"] + note_array["duration_sec"] + duration = note_array["duration_sec"] + + pitch = note_array["pitch"] + + # Duration of the piece + piece_duration = offsets.max() + + # Number of frames + num_frames = int(np.round(piece_duration * samplerate)) + + # Initialize array containing audio + audio_signal = np.zeros(num_frames, dtype="float") + + # Initialize the time axis + x = np.linspace(0, piece_duration, num=num_frames) + + # onsets in frames (i.e., indices of the `audio` array) + onsets_in_frames = np.searchsorted(x, onsets, side="left") + + # frequency of the note in herz + if tuning == "equal_temperament": + freq_in_hz = midi_pitch_to_frequency(pitch, **tuning_kwargs) + elif tuning == "natural": + freq_in_hz = midi_pitch_to_natural_frequency(pitch, **tuning_kwargs) + + if harmonic_dist is None: + + def harmonic_dist(x): + return x, 1 + + elif isinstance(harmonic_dist, int): + + harmonic_dist = DistributedHarmonics(harmonic_dist) + + elif isinstance(harmonic_dist, str): + if harmonic_dist in ("shepard",): + harmonic_dist = ShepardTones() + + for (f, oif, dur) in zip(freq_in_hz, onsets_in_frames, duration): + + freqs, weights = harmonic_dist(f) + + note = additive_synthesis( + freqs=freqs, + duration=dur, + samplerate=samplerate, + weights=weights, + envelope_fun=envelope_fun, + ) + idx = slice(oif, oif + len(note)) + audio_signal[idx] += note + + # normalization term + # TODO: Non-linear normalization? + norm_term = max(audio_signal.max(), abs(audio_signal.min())) + + # normalize audio + audio_signal /= norm_term + + return audio_signal diff --git a/requirements.txt b/requirements.txt index 3b7fa093..10ee666f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ xmlschema lxml lark-parser mido +nbsphinx \ No newline at end of file diff --git a/setup.py b/setup.py index ea18d5ce..e02add30 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ EMAIL = "partitura-users@googlegroups.com" AUTHOR = "Maarten Grachten, Carlos Cancino-Chacón, Silvan Peter, Emmanouil Karystinaios, Francesco Foscarin, Thassilo Gadermaier" REQUIRES_PYTHON = ">=3.6" -VERSION = "1.0.0" +VERSION = "1.1.0" # What packages are required for this module to be executed? REQUIRED = ["numpy", "scipy", "lxml", "lark-parser", "xmlschema", "mido"] diff --git a/tests/__init__.py b/tests/__init__.py index 2c2d594b..f3c3b6ad 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -13,6 +13,9 @@ KERN_PATH = os.path.join(DATA_PATH, "kern") MATCH_PATH = os.path.join(DATA_PATH, "match") NAKAMURA_PATH = os.path.join(DATA_PATH, "nakamura") +MIDI_PATH = os.path.join(DATA_PATH, "midi") +PARANGONADA_PATH = os.path.join(DATA_PATH, "parangonada") +WAV_PATH = os.path.join(DATA_PATH, "wav") # this is a list of files for which importing and subsequent exporting should # yield identical MusicXML @@ -148,7 +151,7 @@ ] ] -KERN_TESFILES = [ +KERN_TESTFILES = [ os.path.join(KERN_PATH, fn) for fn in [ "single_voice_example.krn", @@ -163,3 +166,25 @@ ] KERN_TIES = [os.path.join(KERN_PATH, fn) for fn in ["tie_mismatch.krn"]] + + +MOZART_VARIATION_FILES = dict( + musicxml=os.path.join(MUSICXML_PATH, "mozart_k265_var1.musicxml"), + midi=os.path.join(MIDI_PATH, "mozart_k265_var1.mid"), + match=os.path.join(MATCH_PATH, "mozart_k265_var1.match"), + parangonada_align=os.path.join(PARANGONADA_PATH, "mozart_k265_var1", "align.csv"), + parangonada_feature=os.path.join( + PARANGONADA_PATH, "mozart_k265_var1", "feature.csv" + ), + parangonada_spart=os.path.join(PARANGONADA_PATH, "mozart_k265_var1", "part.csv"), + parangonada_ppart=os.path.join(PARANGONADA_PATH, "mozart_k265_var1", "ppart.csv"), + parangonada_zalign=os.path.join(PARANGONADA_PATH, "mozart_k265_var1", "zalign.csv"), +) + + +WAV_TESTFILES = [ + os.path.join(WAV_PATH, fn) + for fn in [ + "example_linear_equal_temperament_sr8000.wav", + ] +] diff --git a/tests/data/match/mozart_k265_var1.match b/tests/data/match/mozart_k265_var1.match new file mode 100644 index 00000000..818ba3dd --- /dev/null +++ b/tests/data/match/mozart_k265_var1.match @@ -0,0 +1,299 @@ +info(matchFileVersion,5.0). +info(midiClockUnits,480). +info(midiClockRate,500000). +info(keySignature,[C Maj/A min]). +info(timeSignature,[2/4]). +snote(n9,[C,n],3,1:1,0,1/4,0.0,1.0,[])-note(n0,[C,n],3,683,747,747,70). +snote(n1,[D,n],5,1:1,0,1/16,0.0,0.25,[])-note(n1,[D,n],5,692,773,773,72). +snote(n2,[C,n],5,1:1,1/16,1/16,0.25,0.5,[])-note(n2,[C,n],5,819,892,892,53). +snote(n3,[B,n],4,1:1,1/8,1/16,0.5,0.75,[])-note(n3,[B,n],4,914,980,980,54). +snote(n4,[C,n],5,1:1,3/16,1/16,0.75,1.0,[])-note(n4,[C,n],5,1009,1098,1098,48). +snote(n10,[C,n],4,1:2,0,1/4,1.0,2.0,[])-note(n5,[C,n],4,1122,1160,1160,67). +snote(n5,[B,n],4,1:2,0,1/16,1.0,1.25,[])-note(n6,[B,n],4,1128,1174,1174,61). +snote(n6,[C,n],5,1:2,1/16,1/16,1.25,1.5,[])-note(n7,[C,n],5,1233,1284,1284,44). +snote(n7,[B,n],4,1:2,1/8,1/16,1.5,1.75,[])-note(n8,[B,n],4,1316,1410,1410,65). +snote(n8,[C,n],5,1:2,3/16,1/16,1.75,2.0,[])-note(n9,[C,n],5,1420,1490,1490,61). +snote(n19,[E,n],4,2:1,0,1/4,2.0,3.0,[])-note(n10,[E,n],4,1541,1614,1614,75). +snote(n11,[A,n],5,2:1,0,1/16,2.0,2.25,[])-note(n11,[A,n],5,1556,1637,1637,55). +snote(n12,[G,n],5,2:1,1/16,1/16,2.25,2.5,[])-note(n12,[G,n],5,1683,1752,1752,62). +snote(n13,[F,#],5,2:1,1/8,1/16,2.5,2.75,[])-note(n13,[F,#],5,1785,1831,1831,52). +snote(n14,[G,n],5,2:1,3/16,1/16,2.75,3.0,[])-note(n14,[G,n],5,1893,1949,1949,77). +snote(n15,[F,#],5,2:2,0,1/16,3.0,3.25,[])-note(n15,[F,#],5,1984,2041,2041,60). +snote(n20,[C,n],4,2:2,0,1/4,3.0,4.0,[])-note(n16,[C,n],4,2002,2043,2043,49). +snote(n16,[G,n],5,2:2,1/16,1/16,3.25,3.5,[])-note(n17,[G,n],5,2100,2130,2130,52). +snote(n17,[F,#],5,2:2,1/8,1/16,3.5,3.75,[])-note(n18,[F,#],5,2180,2235,2235,64). +snote(n18,[G,n],5,2:2,3/16,1/16,3.75,4.0,[])-note(n19,[G,n],5,2275,2317,2317,54). +snote(n21,[G,#],5,3:1,0,1/16,4.0,4.25,[])-note(n20,[G,#],5,2392,2454,2454,61). +snote(n29,[F,n],4,3:1,0,1/4,4.0,5.0,[])-note(n21,[F,n],4,2416,2457,2457,61). +snote(n22,[A,n],5,3:1,1/16,1/16,4.25,4.5,[])-note(n22,[A,n],5,2495,2600,2600,61). +snote(n23,[C,n],6,3:1,1/8,1/16,4.5,4.75,[])-note(n23,[C,n],6,2611,2722,2722,62). +snote(n24,[B,n],5,3:1,3/16,1/16,4.75,5.0,[])-note(n24,[B,n],5,2722,2796,2796,51). +snote(n25,[D,n],6,3:2,0,1/16,5.0,5.25,[])-note(n25,[D,n],6,2807,2939,2939,61). +snote(n30,[C,n],4,3:2,0,1/4,5.0,6.0,[])-note(n26,[C,n],4,2836,2890,2890,45). +snote(n26,[C,n],6,3:2,1/16,1/16,5.25,5.5,[])-note(n27,[C,n],6,2939,3024,3024,55). +snote(n27,[B,n],5,3:2,1/8,1/16,5.5,5.75,[])-note(n28,[B,n],5,3033,3093,3093,64). +snote(n28,[A,n],5,3:2,3/16,1/16,5.75,6.0,[])-note(n29,[A,n],5,3138,3162,3162,52). +snote(n39,[C,n],4,4:1,0,1/4,6.0,7.0,[])-note(n30,[C,n],4,3275,3538,4094,42). +snote(n31,[A,n],5,4:1,0,1/16,6.0,6.25,[])-note(n31,[A,n],5,3280,3364,4094,68). +snote(n40,[E,n],4,4:1,0,1/4,6.0,7.0,[])-note(n32,[E,n],4,3291,3526,4094,59). +snote(n32,[G,n],5,4:1,1/16,1/16,6.25,6.5,[])-note(n33,[G,n],5,3413,3499,4094,62). +snote(n33,[E,n],6,4:1,1/8,1/16,6.5,6.75,[])-note(n34,[E,n],6,3531,3657,4094,69). +snote(n34,[D,n],6,4:1,3/16,1/16,6.75,7.0,[])-note(n35,[D,n],6,3639,3762,4094,74). +snote(n35,[C,n],6,4:2,0,1/16,7.0,7.25,[])-note(n36,[C,n],6,3758,3859,4094,62). +snote(n36,[B,n],5,4:2,1/16,1/16,7.25,7.5,[])-note(n37,[B,n],5,3845,3912,4094,64). +snote(n37,[A,n],5,4:2,1/8,1/16,7.5,7.75,[])-note(n38,[A,n],5,3933,3976,4094,59). +snote(n42,[C,#],4,4:2,3/16,1/16,7.75,8.0,[])-note(n39,[C,#],4,4050,4094,4094,60). +snote(n38,[G,n],5,4:2,3/16,1/16,7.75,8.0,[])-note(n40,[G,n],5,4058,4069,4094,58). +snote(n43,[G,n],5,5:1,0,1/16,8.0,8.25,[])-note(n41,[G,n],5,4187,4262,4262,63). +snote(n51,[D,n],4,5:1,0,1/4,8.0,9.0,[])-note(n42,[D,n],4,4203,4563,4958,79). +snote(n44,[F,n],5,5:1,1/16,1/16,8.25,8.5,[])-note(n43,[F,n],5,4289,4381,4381,87). +snote(n45,[D,n],6,5:1,1/8,1/16,8.5,8.75,[])-note(n44,[D,n],6,4410,4548,4958,80). +snote(n46,[C,n],6,5:1,3/16,1/16,8.75,9.0,[])-note(n45,[C,n],6,4531,4679,4958,70). +snote(n47,[B,n],5,5:2,0,1/16,9.0,9.25,[])-note(n46,[B,n],5,4637,4736,4958,67). +snote(n48,[A,n],5,5:2,1/16,1/16,9.25,9.5,[])-note(n47,[A,n],5,4730,4810,4958,72). +snote(n49,[G,n],5,5:2,1/8,1/16,9.5,9.75,[])-note(n48,[G,n],5,4838,4876,4958,61). +snote(n50,[F,n],5,5:2,3/16,1/16,9.75,10.0,[])-note(n49,[F,n],5,4939,4961,4961,68). +snote(n53,[B,n],3,5:2,3/16,1/16,9.75,10.0,[])-note(n50,[B,n],3,4944,4981,4981,51). +snote(n54,[F,n],5,6:1,0,1/16,10.0,10.25,[])-note(n51,[F,n],5,5062,5144,5144,79). +snote(n62,[C,n],4,6:1,0,1/4,10.0,11.0,[])-note(n52,[C,n],4,5099,5501,5706,64). +snote(n55,[E,n],5,6:1,1/16,1/16,10.25,10.5,[])-note(n53,[E,n],5,5176,5260,5260,72). +snote(n56,[C,n],6,6:1,1/8,1/16,10.5,10.75,[])-note(n54,[C,n],6,5303,5436,5706,69). +snote(n57,[B,n],5,6:1,3/16,1/16,10.75,11.0,[])-note(n55,[B,n],5,5407,5552,5706,68). +snote(n58,[A,n],5,6:2,0,1/16,11.0,11.25,[])-note(n56,[A,n],5,5509,5611,5706,61). +snote(n59,[G,n],5,6:2,1/16,1/16,11.25,11.5,[])-note(n57,[G,n],5,5596,5677,5706,70). +snote(n60,[F,n],5,6:2,1/8,1/16,11.5,11.75,[])-note(n58,[F,n],5,5684,5753,5753,82). +snote(n64,[A,n],3,6:2,3/16,1/16,11.75,12.0,[])-note(n59,[A,n],3,5793,5854,5854,65). +snote(n61,[E,n],5,6:2,3/16,1/16,11.75,12.0,[])-note(n60,[E,n],5,5809,5882,5882,61). +snote(n65,[D,n],5,7:1,0,1/8,12.0,12.5,[])-note(n61,[D,n],5,5922,6176,6176,75). +snote(n69,[F,n],3,7:1,0,1/4,12.0,13.0,[])-note(n62,[F,n],3,5978,6219,6219,63). +snote(n66,[A,n],5,7:1,1/8,1/8,12.5,13.0,[])-note(n63,[A,n],5,6197,6454,6454,70). +snote(n70,[G,n],3,7:2,0,1/4,13.0,14.0,[])-note(n64,[G,n],3,6416,6610,6610,60). +snote(n67,[G,n],5,7:2,0,1/8,13.0,13.5,[])-note(n65,[G,n],5,6420,6614,6614,77). +snote(n68,[B,n],4,7:2,1/8,1/8,13.5,14.0,[])-note(n66,[B,n],4,6643,6850,6850,69). +snote(n71,[C,n],5,8:1,0,1/4,14.0,15.0,[])-note(n67,[C,n],5,6898,7487,7866,51). +snote(n73,[C,n],4,8:1,0,1/4,14.0,15.0,[])-note(n68,[C,n],4,6911,7028,7028,49). +snote(n74,[C,n],3,8:2,0,1/4,15.0,16.0,[])-note(n69,[C,n],3,7434,7484,7866,42). +snote(n83,[E,n],4,9:1,0,1/4,16.0,17.0,[])-note(n70,[E,n],4,7920,7951,7951,63). +snote(n75,[A,n],5,9:1,0,1/16,16.0,16.25,[])-note(n71,[A,n],5,7928,7983,7983,53). +snote(n76,[G,n],5,9:1,1/16,1/16,16.25,16.5,[])-note(n72,[G,n],5,8012,8083,8083,61). +snote(n77,[F,#],5,9:1,1/8,1/16,16.5,16.75,[])-note(n73,[F,#],5,8126,8180,8180,67). +snote(n78,[G,n],5,9:1,3/16,1/16,16.75,17.0,[])-note(n74,[G,n],5,8258,8298,8298,53). +snote(n79,[F,#],5,9:2,0,1/16,17.0,17.25,[])-note(n75,[F,#],5,8358,8440,8787,52). +snote(n84,[G,n],3,9:2,0,1/4,17.0,18.0,[])-note(n76,[G,n],3,8362,8437,8787,54). +snote(n80,[G,n],5,9:2,1/16,1/16,17.25,17.5,[])-note(n77,[G,n],5,8459,8619,8787,45). +snote(n81,[A,n],5,9:2,1/8,1/16,17.5,17.75,[])-note(n78,[A,n],5,8565,8679,8787,64). +snote(n82,[G,n],5,9:2,3/16,1/16,17.75,18.0,[])-note(n79,[G,n],5,8673,8701,8787,26). +snote(n93,[D,n],4,10:1,0,1/4,18.0,19.0,[])-note(n80,[D,n],4,8806,8866,8866,68). +snote(n85,[G,n],5,10:1,0,1/16,18.0,18.25,[])-note(n81,[G,n],5,8812,8915,8915,73). +snote(n86,[F,n],5,10:1,1/16,1/16,18.25,18.5,[])-note(n82,[F,n],5,8931,8989,8989,55). +snote(n87,[E,n],5,10:1,1/8,1/16,18.5,18.75,[])-note(n83,[E,n],5,9002,9070,9070,72). +snote(n88,[F,n],5,10:1,3/16,1/16,18.75,19.0,[])-note(n84,[F,n],5,9126,9180,9180,54). +snote(n89,[E,n],5,10:2,0,1/16,19.0,19.25,[])-note(n85,[E,n],5,9206,9295,9566,65). +snote(n94,[G,n],3,10:2,0,1/4,19.0,20.0,[])-note(n86,[G,n],3,9256,9354,9566,49). +snote(n90,[F,n],5,10:2,1/16,1/16,19.25,19.5,[])-note(n87,[F,n],5,9325,9384,9566,52). +snote(n91,[G,n],5,10:2,1/8,1/16,19.5,19.75,[])-note(n88,[G,n],5,9415,9492,9566,63). +snote(n92,[F,n],5,10:2,3/16,1/16,19.75,20.0,[])-note(n89,[F,n],5,9500,9542,9566,52). +snote(n95,[F,n],5,11:1,0,1/16,20.0,20.25,[])-note(n90,[F,n],5,9649,9768,9768,65). +snote(n103,[C,n],4,11:1,0,1/4,20.0,21.0,[])-note(n91,[C,n],4,9665,9719,9719,65). +snote(n96,[E,n],5,11:1,1/16,1/16,20.25,20.5,[])-note(n92,[E,n],5,9763,9859,9859,67). +snote(n97,[D,#],5,11:1,1/8,1/16,20.5,20.75,[])-note(n93,[D,#],5,9870,9956,9956,64). +snote(n98,[E,n],5,11:1,3/16,1/16,20.75,21.0,[])-note(n94,[E,n],5,9988,10056,10056,55). +snote(n104,[G,n],3,11:2,0,1/4,21.0,22.0,[])-note(n95,[G,n],3,10085,10192,10192,60). +snote(n99,[D,#],5,11:2,0,1/16,21.0,21.25,[])-note(n96,[D,#],5,10090,10183,10183,49). +snote(n100,[E,n],5,11:2,1/16,1/16,21.25,21.5,[])-note(n97,[E,n],5,10205,10265,10265,58). +snote(n101,[F,n],5,11:2,1/8,1/16,21.5,21.75,[])-note(n98,[F,n],5,10313,10408,10408,65). +snote(n102,[E,n],5,11:2,3/16,1/16,21.75,22.0,[])-note(n99,[E,n],5,10394,10424,10424,52). +snote(n105,[E,n],5,12:1,0,1/16,22.0,22.25,[])-note(n100,[E,n],5,10527,10607,10607,78). +snote(n113,[F,n],4,12:1,0,1/4,22.0,23.0,[])-note(n101,[F,n],4,10540,10615,10615,63). +snote(n106,[D,n],5,12:1,1/16,1/16,22.25,22.5,[])-note(n102,[D,n],5,10646,10711,10711,58). +snote(n107,[C,#],5,12:1,1/8,1/16,22.5,22.75,[])-note(n103,[C,#],5,10755,10818,10818,55). +snote(n108,[D,n],5,12:1,3/16,1/16,22.75,23.0,[])-note(n104,[D,n],5,10870,10910,10910,48). +snote(n109,[C,#],5,12:2,0,1/16,23.0,23.25,[])-note(n105,[C,#],5,10958,11052,11323,55). +snote(n114,[G,n],3,12:2,0,1/4,23.0,24.0,[])-note(n106,[G,n],3,10993,11084,11323,49). +snote(n110,[D,n],5,12:2,1/16,1/16,23.25,23.5,[])-note(n107,[D,n],5,11095,11115,11323,39). +snote(n111,[E,n],5,12:2,1/8,1/16,23.5,23.75,[])-note(n108,[E,n],5,11161,11217,11323,55). +snote(n112,[D,n],5,12:2,3/16,1/16,23.75,24.0,[])-note(n109,[D,n],5,11233,11283,11323,61). +snote(n124,[E,n],4,13:1,0,1/2,24.0,26.0,[])-note(n110,[E,n],4,11400,12020,12244,60). +snote(n123,[G,n],3,13:1,0,1/2,24.0,26.0,[])-note(n111,[G,n],3,11406,12019,12244,53). +snote(n115,[A,n],5,13:1,0,1/16,24.0,24.25,[])-note(n112,[A,n],5,11420,11494,11494,67). +snote(n116,[G,n],5,13:1,1/16,1/16,24.25,24.5,[])-note(n113,[G,n],5,11536,11590,11590,66). +snote(n117,[F,#],5,13:1,1/8,1/16,24.5,24.75,[])-note(n114,[F,#],5,11643,11701,12244,52). +snote(n118,[G,n],5,13:1,3/16,1/16,24.75,25.0,[])-note(n115,[G,n],5,11731,11808,12244,69). +snote(n119,[E,n],6,13:2,0,1/16,25.0,25.25,[])-note(n116,[E,n],6,11851,11976,12244,77). +snote(n120,[C,n],6,13:2,1/16,1/16,25.25,25.5,[])-note(n117,[C,n],6,11963,12107,12244,58). +snote(n121,[A,n],5,13:2,1/8,1/16,25.5,25.75,[])-note(n118,[A,n],5,12068,12150,12244,66). +snote(n122,[G,n],5,13:2,3/16,1/16,25.75,26.0,[])-note(n119,[G,n],5,12149,12197,12244,64). +snote(n133,[G,n],3,14:1,0,1/2,26.0,28.0,[])-note(n120,[G,n],3,12304,12924,13194,54). +snote(n134,[D,n],4,14:1,0,1/2,26.0,28.0,[])-note(n121,[D,n],4,12313,12949,13194,61). +snote(n125,[G,n],5,14:1,0,1/16,26.0,26.25,[])-note(n122,[G,n],5,12315,12393,12393,75). +snote(n126,[F,n],5,14:1,1/16,1/16,26.25,26.5,[])-note(n123,[F,n],5,12425,12501,12501,73). +snote(n127,[E,n],5,14:1,1/8,1/16,26.5,26.75,[])-note(n124,[E,n],5,12548,12614,12614,53). +snote(n128,[F,n],5,14:1,3/16,1/16,26.75,27.0,[])-note(n125,[F,n],5,12643,12723,13194,77). +snote(n129,[D,n],6,14:2,0,1/16,27.0,27.25,[])-note(n126,[D,n],6,12762,12910,13194,79). +snote(n130,[B,n],5,14:2,1/16,1/16,27.25,27.5,[])-note(n127,[B,n],5,12884,13010,13194,59). +snote(n131,[G,n],5,14:2,1/8,1/16,27.5,27.75,[])-note(n128,[G,n],5,12988,13049,13194,64). +snote(n132,[F,n],5,14:2,3/16,1/16,27.75,28.0,[])-note(n129,[F,n],5,13061,13104,13194,62). +snote(n135,[F,n],5,15:1,0,1/16,28.0,28.25,[])-note(n130,[F,n],5,13215,13299,13299,64). +snote(n143,[G,n],3,15:1,0,1/4,28.0,29.0,[])-note(n131,[G,n],3,13227,13754,14145,45). +snote(n144,[C,n],4,15:1,0,1/4,28.0,29.0,[])-note(n132,[C,n],4,13229,13761,14145,42). +snote(n136,[E,n],5,15:1,1/16,1/16,28.25,28.5,[])-note(n133,[E,n],5,13343,13394,13394,52). +snote(n137,[D,#],5,15:1,1/8,1/16,28.5,28.75,[])-note(n134,[D,#],5,13449,13547,14145,61). +snote(n138,[E,n],5,15:1,3/16,1/16,28.75,29.0,[])-note(n135,[E,n],5,13570,13709,14145,58). +snote(n139,[C,n],6,15:2,0,1/16,29.0,29.25,[])-note(n136,[C,n],6,13666,13790,14145,67). +insertion-note(n137,[A,n],5,13852,13931,14145,23). +snote(n140,[G,n],5,15:2,1/16,1/16,29.25,29.5,[])-note(n138,[G,n],5,13854,13892,14145,31). +snote(n141,[F,n],5,15:2,1/8,1/16,29.5,29.75,[])-note(n139,[F,n],5,13900,13997,14145,55). +snote(n146,[C,n],4,15:2,3/16,1/16,29.75,30.0,[])-note(n140,[C,n],4,13990,14130,14145,52). +snote(n142,[E,n],5,15:2,3/16,1/16,29.75,30.0,[])-note(n141,[E,n],5,14007,14217,14217,62). +snote(n153,[G,n],3,16:1,0,1/2,30.0,32.0,[])-note(n142,[G,n],3,14181,14897,14897,45). +snote(n150,[E,n],4,16:1,0,3/16,30.0,30.75,[])-note(n143,[E,n],4,14198,14709,14709,44). +snote(n147,[G,n],5,16:1,0,3/16,30.0,30.75,[])-note(n144,[G,n],5,14219,14738,14738,68). +snote(n151,[C,n],4,16:1,3/16,1/16,30.75,31.0,[])-note(n145,[C,n],4,14730,14872,14872,43). +snote(n148,[E,n],5,16:1,3/16,1/16,30.75,31.0,[])-note(n146,[E,n],5,14733,14901,14901,47). +snote(n152,[B,n],3,16:2,0,1/4,31.0,32.0,[])-note(n147,[B,n],3,14892,15103,15498,51). +snote(n149,[D,n],5,16:2,0,1/4,31.0,32.0,[])-note(n148,[D,n],5,14894,15140,15498,44). +snote(n162,[C,n],3,17:1,0,1/4,32.0,33.0,[])-note(n149,[C,n],3,15585,15661,15661,61). +snote(n154,[D,n],5,17:1,0,1/16,32.0,32.25,[])-note(n150,[D,n],5,15590,15672,15672,58). +snote(n155,[C,n],5,17:1,1/16,1/16,32.25,32.5,[])-note(n151,[C,n],5,15720,15756,15756,39). +snote(n156,[B,n],4,17:1,1/8,1/16,32.5,32.75,[])-note(n152,[B,n],4,15800,15856,15856,54). +snote(n157,[C,n],5,17:1,3/16,1/16,32.75,33.0,[])-note(n153,[C,n],5,15895,15993,15993,55). +snote(n163,[C,n],4,17:2,0,1/4,33.0,34.0,[])-note(n154,[C,n],4,16014,16068,16068,59). +snote(n158,[B,n],4,17:2,0,1/16,33.0,33.25,[])-note(n155,[B,n],4,16026,16096,16096,48). +snote(n159,[C,n],5,17:2,1/16,1/16,33.25,33.5,[])-note(n156,[C,n],5,16170,16214,16214,43). +snote(n160,[B,n],4,17:2,1/8,1/16,33.5,33.75,[])-note(n157,[B,n],4,16255,16315,16315,59). +snote(n161,[C,n],5,17:2,3/16,1/16,33.75,34.0,[])-note(n158,[C,n],5,16355,16377,16377,49). +snote(n172,[E,n],4,18:1,0,1/4,34.0,35.0,[])-note(n159,[E,n],4,16437,16500,16500,59). +snote(n164,[A,n],5,18:1,0,1/16,34.0,34.25,[])-note(n160,[A,n],5,16460,16530,16530,25). +snote(n165,[G,n],5,18:1,1/16,1/16,34.25,34.5,[])-note(n161,[G,n],5,16576,16647,16647,63). +snote(n166,[F,#],5,18:1,1/8,1/16,34.5,34.75,[])-note(n162,[F,#],5,16674,16734,16734,58). +snote(n167,[G,n],5,18:1,3/16,1/16,34.75,35.0,[])-note(n163,[G,n],5,16802,16826,16826,42). +snote(n168,[F,#],5,18:2,0,1/16,35.0,35.25,[])-note(n164,[F,#],5,16897,16950,16950,54). +snote(n173,[C,n],4,18:2,0,1/4,35.0,36.0,[])-note(n165,[C,n],4,16900,16961,16961,47). +snote(n169,[G,n],5,18:2,1/16,1/16,35.25,35.5,[])-note(n166,[G,n],5,17000,17030,17030,44). +snote(n170,[F,#],5,18:2,1/8,1/16,35.5,35.75,[])-note(n167,[F,#],5,17095,17149,17149,55). +snote(n171,[G,n],5,18:2,3/16,1/16,35.75,36.0,[])-note(n168,[G,n],5,17227,17239,17239,32). +snote(n174,[G,#],5,19:1,0,1/16,36.0,36.25,[])-note(n169,[G,#],5,17291,17379,17379,60). +snote(n182,[F,n],4,19:1,0,1/4,36.0,37.0,[])-note(n170,[F,n],4,17318,17379,17379,50). +snote(n175,[A,n],5,19:1,1/16,1/16,36.25,36.5,[])-note(n171,[A,n],5,17413,17516,17516,64). +snote(n176,[C,n],6,19:1,1/8,1/16,36.5,36.75,[])-note(n172,[C,n],6,17515,17625,17625,60). +snote(n177,[B,n],5,19:1,3/16,1/16,36.75,37.0,[])-note(n173,[B,n],5,17639,17715,17715,44). +snote(n178,[D,n],6,19:2,0,1/16,37.0,37.25,[])-note(n174,[D,n],6,17721,17846,17846,61). +snote(n183,[C,n],4,19:2,0,1/4,37.0,38.0,[])-note(n175,[C,n],4,17737,17822,17822,46). +snote(n179,[C,n],6,19:2,1/16,1/16,37.25,37.5,[])-note(n176,[C,n],6,17871,17948,17948,42). +snote(n180,[B,n],5,19:2,1/8,1/16,37.5,37.75,[])-note(n177,[B,n],5,17953,18033,18033,55). +snote(n181,[A,n],5,19:2,3/16,1/16,37.75,38.0,[])-note(n178,[A,n],5,18035,18067,18067,58). +snote(n192,[C,n],4,20:1,0,1/4,38.0,39.0,[])-note(n179,[C,n],4,18194,18582,18955,48). +snote(n193,[E,n],4,20:1,0,1/4,38.0,39.0,[])-note(n180,[E,n],4,18201,18543,18955,55). +snote(n184,[A,n],5,20:1,0,1/16,38.0,38.25,[])-note(n181,[A,n],5,18201,18252,18252,55). +snote(n185,[G,n],5,20:1,1/16,1/16,38.25,38.5,[])-note(n182,[G,n],5,18290,18385,18955,67). +snote(n186,[E,n],6,20:1,1/8,1/16,38.5,38.75,[])-note(n183,[E,n],6,18420,18561,18955,66). +snote(n187,[D,n],6,20:1,3/16,1/16,38.75,39.0,[])-note(n184,[D,n],6,18532,18660,18955,63). +snote(n188,[C,n],6,20:2,0,1/16,39.0,39.25,[])-note(n185,[C,n],6,18632,18742,18955,71). +snote(n189,[B,n],5,20:2,1/16,1/16,39.25,39.5,[])-note(n186,[B,n],5,18721,18794,18955,68). +snote(n190,[A,n],5,20:2,1/8,1/16,39.5,39.75,[])-note(n187,[A,n],5,18813,18856,18955,60). +snote(n195,[C,#],4,20:2,3/16,1/16,39.75,40.0,[])-note(n188,[C,#],4,18940,18986,18986,58). +snote(n191,[G,n],5,20:2,3/16,1/16,39.75,40.0,[])-note(n189,[G,n],5,18948,18977,18977,71). +snote(n196,[G,n],5,21:1,0,1/16,40.0,40.25,[])-note(n190,[G,n],5,19065,19157,19157,77). +snote(n204,[D,n],4,21:1,0,1/4,40.0,41.0,[])-note(n191,[D,n],4,19087,19522,19819,74). +snote(n197,[F,n],5,21:1,1/16,1/16,40.25,40.5,[])-note(n192,[F,n],5,19185,19289,19289,84). +snote(n198,[D,n],6,21:1,1/8,1/16,40.5,40.75,[])-note(n193,[D,n],6,19293,19411,19819,75). +snote(n199,[C,n],6,21:1,3/16,1/16,40.75,41.0,[])-note(n194,[C,n],6,19409,19543,19819,62). +snote(n200,[B,n],5,21:2,0,1/16,41.0,41.25,[])-note(n195,[B,n],5,19506,19618,19819,71). +snote(n201,[A,n],5,21:2,1/16,1/16,41.25,41.5,[])-note(n196,[A,n],5,19616,19671,19819,45). +snote(n202,[G,n],5,21:2,1/8,1/16,41.5,41.75,[])-note(n197,[G,n],5,19679,19734,19819,70). +snote(n203,[F,n],5,21:2,3/16,1/16,41.75,42.0,[])-note(n198,[F,n],5,19810,19845,19845,81). +snote(n206,[B,n],3,21:2,3/16,1/16,41.75,42.0,[])-note(n199,[B,n],3,19840,19876,19876,47). +snote(n207,[F,n],5,22:1,0,1/16,42.0,42.25,[])-note(n200,[F,n],5,19948,20032,20032,77). +snote(n215,[C,n],4,22:1,0,1/4,42.0,43.0,[])-note(n201,[C,n],4,19981,20353,20682,69). +snote(n208,[E,n],5,22:1,1/16,1/16,42.25,42.5,[])-note(n202,[E,n],5,20053,20156,20682,77). +snote(n209,[C,n],6,22:1,1/8,1/16,42.5,42.75,[])-note(n203,[C,n],6,20176,20270,20682,68). +snote(n210,[B,n],5,22:1,3/16,1/16,42.75,43.0,[])-note(n204,[B,n],5,20276,20417,20682,62). +snote(n211,[A,n],5,22:2,0,1/16,43.0,43.25,[])-note(n205,[A,n],5,20379,20485,20682,59). +snote(n212,[G,n],5,22:2,1/16,1/16,43.25,43.5,[])-note(n206,[G,n],5,20462,20534,20682,71). +snote(n213,[F,n],5,22:2,1/8,1/16,43.5,43.75,[])-note(n207,[F,n],5,20557,20635,20682,72). +snote(n214,[E,n],5,22:2,3/16,1/16,43.75,44.0,[])-note(n208,[E,n],5,20663,20743,20743,76). +snote(n217,[A,n],3,22:2,3/16,1/16,43.75,44.0,[])-note(n209,[A,n],3,20675,20787,20787,61). +snote(n218,[D,n],5,23:1,0,1/8,44.0,44.5,[])-note(n210,[D,n],5,20796,21067,21067,64). +snote(n222,[F,n],3,23:1,0,1/4,44.0,45.0,[])-note(n211,[F,n],3,20854,21113,21113,72). +snote(n219,[A,n],5,23:1,1/8,1/8,44.5,45.0,[])-note(n212,[A,n],5,21064,21476,21476,68). +snote(n223,[G,n],3,23:2,0,1/4,45.0,46.0,[])-note(n213,[G,n],3,21287,21555,21555,70). +snote(n220,[G,n],5,23:2,0,1/8,45.0,45.5,[])-note(n214,[G,n],5,21296,21543,21543,75). +snote(n221,[B,n],4,23:2,1/8,1/8,45.5,46.0,[])-note(n215,[B,n],4,21540,21748,21748,58). +snote(n224,[C,n],5,24:1,0,1/4,46.0,47.0,[])-note(n216,[C,n],5,21793,22915,22915,48). +snote(n226,[C,n],4,24:1,0,1/4,46.0,47.0,[])-note(n217,[C,n],4,21819,22029,22029,49). +snote(n227,[C,n],3,24:2,0,1/4,47.0,48.0,[])-note(n218,[C,n],3,22393,22966,22966,50). +meta(timeSignature,2/4,1,0.0). +meta(keySignature,C Maj/A min,1,0.0). +sustain(0,0). +sustain(3316,67). +sustain(3346,127). +sustain(4064,71). +sustain(4094,45). +sustain(4123,0). +sustain(4384,60). +sustain(4412,127). +sustain(4929,73). +sustain(4958,55). +sustain(4987,41). +sustain(5015,0). +sustain(5246,58). +sustain(5276,127). +sustain(5706,60). +sustain(5735,48). +sustain(5763,0). +sustain(7436,69). +sustain(7464,127). +sustain(7866,62). +sustain(7894,44). +sustain(7923,0). +sustain(8385,68). +sustain(8415,127). +sustain(8787,62). +sustain(8817,46). +sustain(8846,0). +sustain(9279,127). +sustain(9566,53). +sustain(9594,40). +sustain(9622,0). +sustain(11006,67). +sustain(11036,127). +sustain(11294,71). +sustain(11323,55). +sustain(11350,0). +sustain(11668,62). +sustain(11698,127). +sustain(12244,53). +sustain(12273,0). +sustain(12590,60). +sustain(12620,127). +sustain(13165,73). +sustain(13194,64). +sustain(13223,53). +sustain(13251,0). +sustain(13511,59). +sustain(13541,127). +sustain(14145,62). +sustain(14174,50). +sustain(14202,37). +sustain(14231,0). +sustain(14923,40). +sustain(14952,67). +sustain(14981,127). +sustain(15498,62). +sustain(15527,53). +sustain(15555,0). +sustain(18262,54). +sustain(18292,71). +sustain(18322,127). +sustain(18955,50). +sustain(18983,0). +sustain(19271,41). +sustain(19301,127). +sustain(19819,57). +sustain(19847,40). +sustain(19876,0). +sustain(20077,43). +sustain(20107,127). +sustain(20682,61). +sustain(20711,50). +sustain(20740,0). diff --git a/tests/data/midi/mozart_k265_var1.mid b/tests/data/midi/mozart_k265_var1.mid new file mode 100644 index 00000000..4d20b412 Binary files /dev/null and b/tests/data/midi/mozart_k265_var1.mid differ diff --git a/tests/data/musicxml/mozart_k265_var1.musicxml b/tests/data/musicxml/mozart_k265_var1.musicxml new file mode 100644 index 00000000..8af5d0b7 --- /dev/null +++ b/tests/data/musicxml/mozart_k265_var1.musicxml @@ -0,0 +1,3120 @@ + + + + + Variations on "Ah, vous dirai-je Maman" K 265 + + + W. A. Mozart + Public Domain + + MuseScore 3.6.2 + 2022-10-04 + + + + + + + + + + 7 + 40 + + + 1697.14 + 1200 + + 85.7143 + 85.7143 + 85.7143 + 85.7143 + + + 85.7143 + 85.7143 + 85.7143 + 85.7143 + + + + + + + title + Variations on "Ah, vous dirai-je Maman" K 265 + + + subtitle + Variation I + + + composer + W. A. Mozart + + + rights + Public Domain + + + + Piano + Pno. + + Piano + + + + 1 + 1 + 78.7402 + 0 + + + + + + + + + 65.90 + 0.00 + + 170.00 + + + 58.75 + + + + 4 + + 0 + + + 2 + + G + 2 + + + F + 4 + + + + + D + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + C + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + B + 4 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + C + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + B + 4 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + C + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + B + 4 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + C + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + C + 3 + + 4 + 5 + quarter + up + 2 + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + + + A + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 1 + 5 + + 1 + 1 + 16th + sharp + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + F + 1 + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 1 + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + E + 4 + + 4 + 5 + quarter + down + 2 + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + + + G + 1 + 5 + + 1 + 1 + 16th + sharp + down + 1 + begin + begin + + + + A + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + C + 6 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + B + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + D + 6 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + C + 6 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + B + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + A + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + F + 4 + + 4 + 5 + quarter + down + 2 + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + + + A + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + + + + G + 5 + + 1 + 1 + 16th + natural + down + 1 + continue + continue + + + + + + + E + 6 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + D + 6 + + 1 + 1 + 16th + down + 1 + end + end + + + + C + 6 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + B + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + A + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + + E + 4 + + 4 + 5 + quarter + down + 2 + + + + 3 + 5 + eighth + + 2 + + + + C + 1 + 4 + + 1 + 5 + 16th + sharp + down + 2 + + + + + + G + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + + + + F + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + + + + D + 6 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + C + 6 + + 1 + 1 + 16th + down + 1 + end + end + + + + B + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + A + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + D + 4 + + 4 + 5 + quarter + down + 2 + + + + 3 + 5 + eighth + + 2 + + + + B + 3 + + 1 + 5 + 16th + down + 2 + + + + + + F + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + + + + E + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + + + + C + 6 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + B + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + A + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + E + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + C + 4 + + 4 + 5 + quarter + natural + down + 2 + + + + 3 + 5 + eighth + + 2 + + + + A + 3 + + 1 + 5 + 16th + down + 2 + + + + + + D + 5 + + 2 + 1 + eighth + down + 1 + begin + + + + A + 5 + + 2 + 1 + eighth + down + 1 + continue + + + + + + + G + 5 + + 2 + 1 + eighth + down + 1 + continue + + + + + + + B + 4 + + 2 + 1 + eighth + down + 1 + end + + + 8 + + + + F + 3 + + 4 + 5 + quarter + down + 2 + + + + G + 3 + + 4 + 5 + quarter + down + 2 + + + + + + C + 5 + + 4 + 1 + quarter + down + 1 + + + + 4 + 1 + quarter + 1 + + + 8 + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + C + 3 + + 4 + 5 + quarter + up + 2 + + + + + + A + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 1 + 5 + + 1 + 1 + 16th + sharp + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + F + 1 + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + A + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + E + 4 + + 4 + 5 + quarter + down + 2 + + + + G + 3 + + 4 + 5 + quarter + down + 2 + + + + + + G + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + F + 5 + + 1 + 1 + 16th + natural + down + 1 + continue + continue + + + + E + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + E + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + F + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + D + 4 + + 4 + 5 + quarter + down + 2 + + + + G + 3 + + 4 + 5 + quarter + down + 2 + + + + + + F + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + E + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + D + 1 + 5 + + 1 + 1 + 16th + sharp + down + 1 + continue + continue + + + + E + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + D + 1 + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + E + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + E + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + G + 3 + + 4 + 5 + quarter + down + 2 + + + + + + E + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + D + 5 + + 1 + 1 + 16th + natural + down + 1 + continue + continue + + + + C + 1 + 5 + + 1 + 1 + 16th + sharp + down + 1 + continue + continue + + + + D + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + C + 1 + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + D + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + E + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + D + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + F + 4 + + 4 + 5 + quarter + down + 2 + + + + G + 3 + + 4 + 5 + quarter + down + 2 + + + + + + A + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 1 + 5 + + 1 + 1 + 16th + sharp + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + E + 6 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + C + 6 + + 1 + 1 + 16th + natural + down + 1 + continue + continue + + + + A + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + G + 3 + + 8 + 5 + half + down + 2 + + + + + E + 4 + + 8 + 5 + half + down + 2 + + + + + + G + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + F + 5 + + 1 + 1 + 16th + natural + down + 1 + continue + continue + + + + E + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + D + 6 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + B + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + G + 3 + + 8 + 5 + half + down + 2 + + + + + D + 4 + + 8 + 5 + half + down + 2 + + + + + + F + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + E + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + D + 1 + 5 + + 1 + 1 + 16th + sharp + down + 1 + continue + continue + + + + E + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + C + 6 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + E + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + G + 3 + + 4 + 5 + quarter + down + 2 + + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + 3 + 5 + eighth + + 2 + + + + C + 4 + + 1 + 5 + 16th + down + 2 + + + + + + G + 5 + + 3 + 1 + eighth + + down + 1 + begin + + + + + + + E + 5 + + 1 + 1 + 16th + down + 1 + end + backward hook + + + + D + 5 + + 4 + 1 + quarter + natural + down + 1 + + + + + + 8 + + + + E + 4 + + 3 + 5 + eighth + + up + 2 + begin + + + + C + 4 + + 1 + 5 + 16th + up + 2 + end + backward hook + + + + B + 3 + + 4 + 5 + quarter + up + 2 + + + 8 + + + + G + 3 + + 8 + 6 + half + down + 2 + + + + + + D + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + C + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + B + 4 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + C + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + B + 4 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + C + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + B + 4 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + C + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + C + 3 + + 4 + 5 + quarter + up + 2 + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + + + A + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 1 + 5 + + 1 + 1 + 16th + sharp + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + F + 1 + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 1 + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + E + 4 + + 4 + 5 + quarter + down + 2 + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + + + G + 1 + 5 + + 1 + 1 + 16th + sharp + down + 1 + begin + begin + + + + A + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + C + 6 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + B + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + D + 6 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + C + 6 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + B + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + A + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + F + 4 + + 4 + 5 + quarter + down + 2 + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + + + A + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + + + + G + 5 + + 1 + 1 + 16th + natural + down + 1 + continue + continue + + + + + + + E + 6 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + D + 6 + + 1 + 1 + 16th + down + 1 + end + end + + + + C + 6 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + B + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + A + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + + E + 4 + + 4 + 5 + quarter + down + 2 + + + + 3 + 5 + eighth + + 2 + + + + C + 1 + 4 + + 1 + 5 + 16th + sharp + down + 2 + + + + + + G + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + + + + F + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + + + + D + 6 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + C + 6 + + 1 + 1 + 16th + down + 1 + end + end + + + + B + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + A + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + D + 4 + + 4 + 5 + quarter + down + 2 + + + + 3 + 5 + eighth + + 2 + + + + B + 3 + + 1 + 5 + 16th + down + 2 + + + + + + F + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + + + + E + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + + + + C + 6 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + B + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + + A + 5 + + 1 + 1 + 16th + down + 1 + begin + begin + + + + G + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + F + 5 + + 1 + 1 + 16th + down + 1 + continue + continue + + + + E + 5 + + 1 + 1 + 16th + down + 1 + end + end + + + 8 + + + + C + 4 + + 4 + 5 + quarter + natural + down + 2 + + + + 3 + 5 + eighth + + 2 + + + + A + 3 + + 1 + 5 + 16th + down + 2 + + + + + + D + 5 + + 2 + 1 + eighth + down + 1 + begin + + + + A + 5 + + 2 + 1 + eighth + down + 1 + continue + + + + + + + G + 5 + + 2 + 1 + eighth + down + 1 + continue + + + + + + + B + 4 + + 2 + 1 + eighth + down + 1 + end + + + 8 + + + + F + 3 + + 4 + 5 + quarter + down + 2 + + + + G + 3 + + 4 + 5 + quarter + down + 2 + + + + + + C + 5 + + 4 + 1 + quarter + down + 1 + + + + 4 + 1 + quarter + 1 + + + 8 + + + + C + 4 + + 4 + 5 + quarter + down + 2 + + + + C + 3 + + 4 + 5 + quarter + up + 2 + + + light-heavy + + + + diff --git a/tests/data/parangonada/mozart_k265_var1/align.csv b/tests/data/parangonada/mozart_k265_var1/align.csv new file mode 100644 index 00000000..078300e2 --- /dev/null +++ b/tests/data/parangonada/mozart_k265_var1/align.csv @@ -0,0 +1,220 @@ +idx,matchtype,partid,ppartid +0,0,n9,n0 +1,0,n1,n1 +2,0,n2,n2 +3,0,n3,n3 +4,0,n4,n4 +5,0,n10,n5 +6,0,n5,n6 +7,0,n6,n7 +8,0,n7,n8 +9,0,n8,n9 +10,0,n19,n10 +11,0,n11,n11 +12,0,n12,n12 +13,0,n13,n13 +14,0,n14,n14 +15,0,n15,n15 +16,0,n20,n16 +17,0,n16,n17 +18,0,n17,n18 +19,0,n18,n19 +20,0,n21,n20 +21,0,n29,n21 +22,0,n22,n22 +23,0,n23,n23 +24,0,n24,n24 +25,0,n25,n25 +26,0,n30,n26 +27,0,n26,n27 +28,0,n27,n28 +29,0,n28,n29 +30,0,n39,n30 +31,0,n31,n31 +32,0,n40,n32 +33,0,n32,n33 +34,0,n33,n34 +35,0,n34,n35 +36,0,n35,n36 +37,0,n36,n37 +38,0,n37,n38 +39,0,n42,n39 +40,0,n38,n40 +41,0,n43,n41 +42,0,n51,n42 +43,0,n44,n43 +44,0,n45,n44 +45,0,n46,n45 +46,0,n47,n46 +47,0,n48,n47 +48,0,n49,n48 +49,0,n50,n49 +50,0,n53,n50 +51,0,n54,n51 +52,0,n62,n52 +53,0,n55,n53 +54,0,n56,n54 +55,0,n57,n55 +56,0,n58,n56 +57,0,n59,n57 +58,0,n60,n58 +59,0,n64,n59 +60,0,n61,n60 +61,0,n65,n61 +62,0,n69,n62 +63,0,n66,n63 +64,0,n70,n64 +65,0,n67,n65 +66,0,n68,n66 +67,0,n71,n67 +68,0,n73,n68 +69,0,n74,n69 +70,0,n83,n70 +71,0,n75,n71 +72,0,n76,n72 +73,0,n77,n73 +74,0,n78,n74 +75,0,n79,n75 +76,0,n84,n76 +77,0,n80,n77 +78,0,n81,n78 +79,0,n82,n79 +80,0,n93,n80 +81,0,n85,n81 +82,0,n86,n82 +83,0,n87,n83 +84,0,n88,n84 +85,0,n89,n85 +86,0,n94,n86 +87,0,n90,n87 +88,0,n91,n88 +89,0,n92,n89 +90,0,n95,n90 +91,0,n103,n91 +92,0,n96,n92 +93,0,n97,n93 +94,0,n98,n94 +95,0,n104,n95 +96,0,n99,n96 +97,0,n100,n97 +98,0,n101,n98 +99,0,n102,n99 +100,0,n105,n100 +101,0,n113,n101 +102,0,n106,n102 +103,0,n107,n103 +104,0,n108,n104 +105,0,n109,n105 +106,0,n114,n106 +107,0,n110,n107 +108,0,n111,n108 +109,0,n112,n109 +110,0,n124,n110 +111,0,n123,n111 +112,0,n115,n112 +113,0,n116,n113 +114,0,n117,n114 +115,0,n118,n115 +116,0,n119,n116 +117,0,n120,n117 +118,0,n121,n118 +119,0,n122,n119 +120,0,n133,n120 +121,0,n134,n121 +122,0,n125,n122 +123,0,n126,n123 +124,0,n127,n124 +125,0,n128,n125 +126,0,n129,n126 +127,0,n130,n127 +128,0,n131,n128 +129,0,n132,n129 +130,0,n135,n130 +131,0,n143,n131 +132,0,n144,n132 +133,0,n136,n133 +134,0,n137,n134 +135,0,n138,n135 +136,0,n139,n136 +137,2,undefined,n137 +138,0,n140,n138 +139,0,n141,n139 +140,0,n146,n140 +141,0,n142,n141 +142,0,n153,n142 +143,0,n150,n143 +144,0,n147,n144 +145,0,n151,n145 +146,0,n148,n146 +147,0,n152,n147 +148,0,n149,n148 +149,0,n162,n149 +150,0,n154,n150 +151,0,n155,n151 +152,0,n156,n152 +153,0,n157,n153 +154,0,n163,n154 +155,0,n158,n155 +156,0,n159,n156 +157,0,n160,n157 +158,0,n161,n158 +159,0,n172,n159 +160,0,n164,n160 +161,0,n165,n161 +162,0,n166,n162 +163,0,n167,n163 +164,0,n168,n164 +165,0,n173,n165 +166,0,n169,n166 +167,0,n170,n167 +168,0,n171,n168 +169,0,n174,n169 +170,0,n182,n170 +171,0,n175,n171 +172,0,n176,n172 +173,0,n177,n173 +174,0,n178,n174 +175,0,n183,n175 +176,0,n179,n176 +177,0,n180,n177 +178,0,n181,n178 +179,0,n192,n179 +180,0,n193,n180 +181,0,n184,n181 +182,0,n185,n182 +183,0,n186,n183 +184,0,n187,n184 +185,0,n188,n185 +186,0,n189,n186 +187,0,n190,n187 +188,0,n195,n188 +189,0,n191,n189 +190,0,n196,n190 +191,0,n204,n191 +192,0,n197,n192 +193,0,n198,n193 +194,0,n199,n194 +195,0,n200,n195 +196,0,n201,n196 +197,0,n202,n197 +198,0,n203,n198 +199,0,n206,n199 +200,0,n207,n200 +201,0,n215,n201 +202,0,n208,n202 +203,0,n209,n203 +204,0,n210,n204 +205,0,n211,n205 +206,0,n212,n206 +207,0,n213,n207 +208,0,n214,n208 +209,0,n217,n209 +210,0,n218,n210 +211,0,n222,n211 +212,0,n219,n212 +213,0,n223,n213 +214,0,n220,n214 +215,0,n221,n215 +216,0,n224,n216 +217,0,n226,n217 +218,0,n227,n218 diff --git a/tests/data/parangonada/mozart_k265_var1/feature.csv b/tests/data/parangonada/mozart_k265_var1/feature.csv new file mode 100644 index 00000000..7c06e0ef --- /dev/null +++ b/tests/data/parangonada/mozart_k265_var1/feature.csv @@ -0,0 +1,219 @@ +velocity,timing,articulation,id +0.0,0.0,0.0,n9 +0.0,0.0,0.0,n1 +0.0,0.0,0.0,n2 +0.0,0.0,0.0,n3 +0.0,0.0,0.0,n4 +0.0,0.0,0.0,n10 +0.0,0.0,0.0,n5 +0.0,0.0,0.0,n6 +0.0,0.0,0.0,n7 +0.0,0.0,0.0,n8 +0.0,0.0,0.0,n19 +0.0,0.0,0.0,n11 +0.0,0.0,0.0,n12 +0.0,0.0,0.0,n13 +0.0,0.0,0.0,n14 +0.0,0.0,0.0,n20 +0.0,0.0,0.0,n15 +0.0,0.0,0.0,n16 +0.0,0.0,0.0,n17 +0.0,0.0,0.0,n18 +0.0,0.0,0.0,n29 +0.0,0.0,0.0,n21 +0.0,0.0,0.0,n22 +0.0,0.0,0.0,n23 +0.0,0.0,0.0,n24 +0.0,0.0,0.0,n30 +0.0,0.0,0.0,n25 +0.0,0.0,0.0,n26 +0.0,0.0,0.0,n27 +0.0,0.0,0.0,n28 +0.0,0.0,0.0,n39 +0.0,0.0,0.0,n40 +0.0,0.0,0.0,n31 +0.0,0.0,0.0,n32 +0.0,0.0,0.0,n33 +0.0,0.0,0.0,n34 +0.0,0.0,0.0,n35 +0.0,0.0,0.0,n36 +0.0,0.0,0.0,n37 +0.0,0.0,0.0,n42 +0.0,0.0,0.0,n38 +0.0,0.0,0.0,n51 +0.0,0.0,0.0,n43 +0.0,0.0,0.0,n44 +0.0,0.0,0.0,n45 +0.0,0.0,0.0,n46 +0.0,0.0,0.0,n47 +0.0,0.0,0.0,n48 +0.0,0.0,0.0,n49 +0.0,0.0,0.0,n53 +0.0,0.0,0.0,n50 +0.0,0.0,0.0,n62 +0.0,0.0,0.0,n54 +0.0,0.0,0.0,n55 +0.0,0.0,0.0,n56 +0.0,0.0,0.0,n57 +0.0,0.0,0.0,n58 +0.0,0.0,0.0,n59 +0.0,0.0,0.0,n60 +0.0,0.0,0.0,n64 +0.0,0.0,0.0,n61 +0.0,0.0,0.0,n69 +0.0,0.0,0.0,n65 +0.0,0.0,0.0,n66 +0.0,0.0,0.0,n70 +0.0,0.0,0.0,n67 +0.0,0.0,0.0,n68 +0.0,0.0,0.0,n73 +0.0,0.0,0.0,n71 +0.0,0.0,0.0,n74 +0.0,0.0,0.0,n83 +0.0,0.0,0.0,n75 +0.0,0.0,0.0,n76 +0.0,0.0,0.0,n77 +0.0,0.0,0.0,n78 +0.0,0.0,0.0,n84 +0.0,0.0,0.0,n79 +0.0,0.0,0.0,n80 +0.0,0.0,0.0,n81 +0.0,0.0,0.0,n82 +0.0,0.0,0.0,n93 +0.0,0.0,0.0,n85 +0.0,0.0,0.0,n86 +0.0,0.0,0.0,n87 +0.0,0.0,0.0,n88 +0.0,0.0,0.0,n94 +0.0,0.0,0.0,n89 +0.0,0.0,0.0,n90 +0.0,0.0,0.0,n91 +0.0,0.0,0.0,n92 +0.0,0.0,0.0,n103 +0.0,0.0,0.0,n95 +0.0,0.0,0.0,n96 +0.0,0.0,0.0,n97 +0.0,0.0,0.0,n98 +0.0,0.0,0.0,n104 +0.0,0.0,0.0,n99 +0.0,0.0,0.0,n100 +0.0,0.0,0.0,n101 +0.0,0.0,0.0,n102 +0.0,0.0,0.0,n113 +0.0,0.0,0.0,n105 +0.0,0.0,0.0,n106 +0.0,0.0,0.0,n107 +0.0,0.0,0.0,n108 +0.0,0.0,0.0,n114 +0.0,0.0,0.0,n109 +0.0,0.0,0.0,n110 +0.0,0.0,0.0,n111 +0.0,0.0,0.0,n112 +0.0,0.0,0.0,n123 +0.0,0.0,0.0,n124 +0.0,0.0,0.0,n115 +0.0,0.0,0.0,n116 +0.0,0.0,0.0,n117 +0.0,0.0,0.0,n118 +0.0,0.0,0.0,n119 +0.0,0.0,0.0,n120 +0.0,0.0,0.0,n121 +0.0,0.0,0.0,n122 +0.0,0.0,0.0,n133 +0.0,0.0,0.0,n134 +0.0,0.0,0.0,n125 +0.0,0.0,0.0,n126 +0.0,0.0,0.0,n127 +0.0,0.0,0.0,n128 +0.0,0.0,0.0,n129 +0.0,0.0,0.0,n130 +0.0,0.0,0.0,n131 +0.0,0.0,0.0,n132 +0.0,0.0,0.0,n143 +0.0,0.0,0.0,n144 +0.0,0.0,0.0,n135 +0.0,0.0,0.0,n136 +0.0,0.0,0.0,n137 +0.0,0.0,0.0,n138 +0.0,0.0,0.0,n139 +0.0,0.0,0.0,n140 +0.0,0.0,0.0,n141 +0.0,0.0,0.0,n146 +0.0,0.0,0.0,n142 +0.0,0.0,0.0,n153 +0.0,0.0,0.0,n150 +0.0,0.0,0.0,n147 +0.0,0.0,0.0,n151 +0.0,0.0,0.0,n148 +0.0,0.0,0.0,n152 +0.0,0.0,0.0,n149 +0.0,0.0,0.0,n162 +0.0,0.0,0.0,n154 +0.0,0.0,0.0,n155 +0.0,0.0,0.0,n156 +0.0,0.0,0.0,n157 +0.0,0.0,0.0,n163 +0.0,0.0,0.0,n158 +0.0,0.0,0.0,n159 +0.0,0.0,0.0,n160 +0.0,0.0,0.0,n161 +0.0,0.0,0.0,n172 +0.0,0.0,0.0,n164 +0.0,0.0,0.0,n165 +0.0,0.0,0.0,n166 +0.0,0.0,0.0,n167 +0.0,0.0,0.0,n173 +0.0,0.0,0.0,n168 +0.0,0.0,0.0,n169 +0.0,0.0,0.0,n170 +0.0,0.0,0.0,n171 +0.0,0.0,0.0,n182 +0.0,0.0,0.0,n174 +0.0,0.0,0.0,n175 +0.0,0.0,0.0,n176 +0.0,0.0,0.0,n177 +0.0,0.0,0.0,n183 +0.0,0.0,0.0,n178 +0.0,0.0,0.0,n179 +0.0,0.0,0.0,n180 +0.0,0.0,0.0,n181 +0.0,0.0,0.0,n192 +0.0,0.0,0.0,n193 +0.0,0.0,0.0,n184 +0.0,0.0,0.0,n185 +0.0,0.0,0.0,n186 +0.0,0.0,0.0,n187 +0.0,0.0,0.0,n188 +0.0,0.0,0.0,n189 +0.0,0.0,0.0,n190 +0.0,0.0,0.0,n195 +0.0,0.0,0.0,n191 +0.0,0.0,0.0,n204 +0.0,0.0,0.0,n196 +0.0,0.0,0.0,n197 +0.0,0.0,0.0,n198 +0.0,0.0,0.0,n199 +0.0,0.0,0.0,n200 +0.0,0.0,0.0,n201 +0.0,0.0,0.0,n202 +0.0,0.0,0.0,n206 +0.0,0.0,0.0,n203 +0.0,0.0,0.0,n215 +0.0,0.0,0.0,n207 +0.0,0.0,0.0,n208 +0.0,0.0,0.0,n209 +0.0,0.0,0.0,n210 +0.0,0.0,0.0,n211 +0.0,0.0,0.0,n212 +0.0,0.0,0.0,n213 +0.0,0.0,0.0,n217 +0.0,0.0,0.0,n214 +0.0,0.0,0.0,n222 +0.0,0.0,0.0,n218 +0.0,0.0,0.0,n219 +0.0,0.0,0.0,n223 +0.0,0.0,0.0,n220 +0.0,0.0,0.0,n221 +0.0,0.0,0.0,n226 +0.0,0.0,0.0,n224 +0.0,0.0,0.0,n227 diff --git a/tests/data/parangonada/mozart_k265_var1/part.csv b/tests/data/parangonada/mozart_k265_var1/part.csv new file mode 100644 index 00000000..d8f9c6e1 --- /dev/null +++ b/tests/data/parangonada/mozart_k265_var1/part.csv @@ -0,0 +1,219 @@ +onset_beat,duration_beat,onset_quarter,duration_quarter,onset_div,duration_div,pitch,voice,id,divs_pq +0.0,1.0,0.0,1.0,0,4,48,5,n9,4 +0.0,0.25,0.0,0.25,0,1,74,1,n1,4 +0.25,0.25,0.25,0.25,1,1,72,1,n2,4 +0.5,0.25,0.5,0.25,2,1,71,1,n3,4 +0.75,0.25,0.75,0.25,3,1,72,1,n4,4 +1.0,1.0,1.0,1.0,4,4,60,5,n10,4 +1.0,0.25,1.0,0.25,4,1,71,1,n5,4 +1.25,0.25,1.25,0.25,5,1,72,1,n6,4 +1.5,0.25,1.5,0.25,6,1,71,1,n7,4 +1.75,0.25,1.75,0.25,7,1,72,1,n8,4 +2.0,1.0,2.0,1.0,8,4,64,5,n19,4 +2.0,0.25,2.0,0.25,8,1,81,1,n11,4 +2.25,0.25,2.25,0.25,9,1,79,1,n12,4 +2.5,0.25,2.5,0.25,10,1,78,1,n13,4 +2.75,0.25,2.75,0.25,11,1,79,1,n14,4 +3.0,1.0,3.0,1.0,12,4,60,5,n20,4 +3.0,0.25,3.0,0.25,12,1,78,1,n15,4 +3.25,0.25,3.25,0.25,13,1,79,1,n16,4 +3.5,0.25,3.5,0.25,14,1,78,1,n17,4 +3.75,0.25,3.75,0.25,15,1,79,1,n18,4 +4.0,1.0,4.0,1.0,16,4,65,5,n29,4 +4.0,0.25,4.0,0.25,16,1,80,1,n21,4 +4.25,0.25,4.25,0.25,17,1,81,1,n22,4 +4.5,0.25,4.5,0.25,18,1,84,1,n23,4 +4.75,0.25,4.75,0.25,19,1,83,1,n24,4 +5.0,1.0,5.0,1.0,20,4,60,5,n30,4 +5.0,0.25,5.0,0.25,20,1,86,1,n25,4 +5.25,0.25,5.25,0.25,21,1,84,1,n26,4 +5.5,0.25,5.5,0.25,22,1,83,1,n27,4 +5.75,0.25,5.75,0.25,23,1,81,1,n28,4 +6.0,1.0,6.0,1.0,24,4,60,5,n39,4 +6.0,1.0,6.0,1.0,24,4,64,5,n40,4 +6.0,0.25,6.0,0.25,24,1,81,1,n31,4 +6.25,0.25,6.25,0.25,25,1,79,1,n32,4 +6.5,0.25,6.5,0.25,26,1,88,1,n33,4 +6.75,0.25,6.75,0.25,27,1,86,1,n34,4 +7.0,0.25,7.0,0.25,28,1,84,1,n35,4 +7.25,0.25,7.25,0.25,29,1,83,1,n36,4 +7.5,0.25,7.5,0.25,30,1,81,1,n37,4 +7.75,0.25,7.75,0.25,31,1,61,5,n42,4 +7.75,0.25,7.75,0.25,31,1,79,1,n38,4 +8.0,1.0,8.0,1.0,32,4,62,5,n51,4 +8.0,0.25,8.0,0.25,32,1,79,1,n43,4 +8.25,0.25,8.25,0.25,33,1,77,1,n44,4 +8.5,0.25,8.5,0.25,34,1,86,1,n45,4 +8.75,0.25,8.75,0.25,35,1,84,1,n46,4 +9.0,0.25,9.0,0.25,36,1,83,1,n47,4 +9.25,0.25,9.25,0.25,37,1,81,1,n48,4 +9.5,0.25,9.5,0.25,38,1,79,1,n49,4 +9.75,0.25,9.75,0.25,39,1,59,5,n53,4 +9.75,0.25,9.75,0.25,39,1,77,1,n50,4 +10.0,1.0,10.0,1.0,40,4,60,5,n62,4 +10.0,0.25,10.0,0.25,40,1,77,1,n54,4 +10.25,0.25,10.25,0.25,41,1,76,1,n55,4 +10.5,0.25,10.5,0.25,42,1,84,1,n56,4 +10.75,0.25,10.75,0.25,43,1,83,1,n57,4 +11.0,0.25,11.0,0.25,44,1,81,1,n58,4 +11.25,0.25,11.25,0.25,45,1,79,1,n59,4 +11.5,0.25,11.5,0.25,46,1,77,1,n60,4 +11.75,0.25,11.75,0.25,47,1,57,5,n64,4 +11.75,0.25,11.75,0.25,47,1,76,1,n61,4 +12.0,1.0,12.0,1.0,48,4,53,5,n69,4 +12.0,0.5,12.0,0.5,48,2,74,1,n65,4 +12.5,0.5,12.5,0.5,50,2,81,1,n66,4 +13.0,1.0,13.0,1.0,52,4,55,5,n70,4 +13.0,0.5,13.0,0.5,52,2,79,1,n67,4 +13.5,0.5,13.5,0.5,54,2,71,1,n68,4 +14.0,1.0,14.0,1.0,56,4,60,5,n73,4 +14.0,1.0,14.0,1.0,56,4,72,1,n71,4 +15.0,1.0,15.0,1.0,60,4,48,5,n74,4 +16.0,1.0,16.0,1.0,64,4,64,5,n83,4 +16.0,0.25,16.0,0.25,64,1,81,1,n75,4 +16.25,0.25,16.25,0.25,65,1,79,1,n76,4 +16.5,0.25,16.5,0.25,66,1,78,1,n77,4 +16.75,0.25,16.75,0.25,67,1,79,1,n78,4 +17.0,1.0,17.0,1.0,68,4,55,5,n84,4 +17.0,0.25,17.0,0.25,68,1,78,1,n79,4 +17.25,0.25,17.25,0.25,69,1,79,1,n80,4 +17.5,0.25,17.5,0.25,70,1,81,1,n81,4 +17.75,0.25,17.75,0.25,71,1,79,1,n82,4 +18.0,1.0,18.0,1.0,72,4,62,5,n93,4 +18.0,0.25,18.0,0.25,72,1,79,1,n85,4 +18.25,0.25,18.25,0.25,73,1,77,1,n86,4 +18.5,0.25,18.5,0.25,74,1,76,1,n87,4 +18.75,0.25,18.75,0.25,75,1,77,1,n88,4 +19.0,1.0,19.0,1.0,76,4,55,5,n94,4 +19.0,0.25,19.0,0.25,76,1,76,1,n89,4 +19.25,0.25,19.25,0.25,77,1,77,1,n90,4 +19.5,0.25,19.5,0.25,78,1,79,1,n91,4 +19.75,0.25,19.75,0.25,79,1,77,1,n92,4 +20.0,1.0,20.0,1.0,80,4,60,5,n103,4 +20.0,0.25,20.0,0.25,80,1,77,1,n95,4 +20.25,0.25,20.25,0.25,81,1,76,1,n96,4 +20.5,0.25,20.5,0.25,82,1,75,1,n97,4 +20.75,0.25,20.75,0.25,83,1,76,1,n98,4 +21.0,1.0,21.0,1.0,84,4,55,5,n104,4 +21.0,0.25,21.0,0.25,84,1,75,1,n99,4 +21.25,0.25,21.25,0.25,85,1,76,1,n100,4 +21.5,0.25,21.5,0.25,86,1,77,1,n101,4 +21.75,0.25,21.75,0.25,87,1,76,1,n102,4 +22.0,1.0,22.0,1.0,88,4,65,5,n113,4 +22.0,0.25,22.0,0.25,88,1,76,1,n105,4 +22.25,0.25,22.25,0.25,89,1,74,1,n106,4 +22.5,0.25,22.5,0.25,90,1,73,1,n107,4 +22.75,0.25,22.75,0.25,91,1,74,1,n108,4 +23.0,1.0,23.0,1.0,92,4,55,5,n114,4 +23.0,0.25,23.0,0.25,92,1,73,1,n109,4 +23.25,0.25,23.25,0.25,93,1,74,1,n110,4 +23.5,0.25,23.5,0.25,94,1,76,1,n111,4 +23.75,0.25,23.75,0.25,95,1,74,1,n112,4 +24.0,2.0,24.0,2.0,96,8,55,5,n123,4 +24.0,2.0,24.0,2.0,96,8,64,5,n124,4 +24.0,0.25,24.0,0.25,96,1,81,1,n115,4 +24.25,0.25,24.25,0.25,97,1,79,1,n116,4 +24.5,0.25,24.5,0.25,98,1,78,1,n117,4 +24.75,0.25,24.75,0.25,99,1,79,1,n118,4 +25.0,0.25,25.0,0.25,100,1,88,1,n119,4 +25.25,0.25,25.25,0.25,101,1,84,1,n120,4 +25.5,0.25,25.5,0.25,102,1,81,1,n121,4 +25.75,0.25,25.75,0.25,103,1,79,1,n122,4 +26.0,2.0,26.0,2.0,104,8,55,5,n133,4 +26.0,2.0,26.0,2.0,104,8,62,5,n134,4 +26.0,0.25,26.0,0.25,104,1,79,1,n125,4 +26.25,0.25,26.25,0.25,105,1,77,1,n126,4 +26.5,0.25,26.5,0.25,106,1,76,1,n127,4 +26.75,0.25,26.75,0.25,107,1,77,1,n128,4 +27.0,0.25,27.0,0.25,108,1,86,1,n129,4 +27.25,0.25,27.25,0.25,109,1,83,1,n130,4 +27.5,0.25,27.5,0.25,110,1,79,1,n131,4 +27.75,0.25,27.75,0.25,111,1,77,1,n132,4 +28.0,1.0,28.0,1.0,112,4,55,5,n143,4 +28.0,1.0,28.0,1.0,112,4,60,5,n144,4 +28.0,0.25,28.0,0.25,112,1,77,1,n135,4 +28.25,0.25,28.25,0.25,113,1,76,1,n136,4 +28.5,0.25,28.5,0.25,114,1,75,1,n137,4 +28.75,0.25,28.75,0.25,115,1,76,1,n138,4 +29.0,0.25,29.0,0.25,116,1,84,1,n139,4 +29.25,0.25,29.25,0.25,117,1,79,1,n140,4 +29.5,0.25,29.5,0.25,118,1,77,1,n141,4 +29.75,0.25,29.75,0.25,119,1,60,5,n146,4 +29.75,0.25,29.75,0.25,119,1,76,1,n142,4 +30.0,2.0,30.0,2.0,120,8,55,6,n153,4 +30.0,0.75,30.0,0.75,120,3,64,5,n150,4 +30.0,0.75,30.0,0.75,120,3,79,1,n147,4 +30.75,0.25,30.75,0.25,123,1,60,5,n151,4 +30.75,0.25,30.75,0.25,123,1,76,1,n148,4 +31.0,1.0,31.0,1.0,124,4,59,5,n152,4 +31.0,1.0,31.0,1.0,124,4,74,1,n149,4 +32.0,1.0,32.0,1.0,128,4,48,5,n162,4 +32.0,0.25,32.0,0.25,128,1,74,1,n154,4 +32.25,0.25,32.25,0.25,129,1,72,1,n155,4 +32.5,0.25,32.5,0.25,130,1,71,1,n156,4 +32.75,0.25,32.75,0.25,131,1,72,1,n157,4 +33.0,1.0,33.0,1.0,132,4,60,5,n163,4 +33.0,0.25,33.0,0.25,132,1,71,1,n158,4 +33.25,0.25,33.25,0.25,133,1,72,1,n159,4 +33.5,0.25,33.5,0.25,134,1,71,1,n160,4 +33.75,0.25,33.75,0.25,135,1,72,1,n161,4 +34.0,1.0,34.0,1.0,136,4,64,5,n172,4 +34.0,0.25,34.0,0.25,136,1,81,1,n164,4 +34.25,0.25,34.25,0.25,137,1,79,1,n165,4 +34.5,0.25,34.5,0.25,138,1,78,1,n166,4 +34.75,0.25,34.75,0.25,139,1,79,1,n167,4 +35.0,1.0,35.0,1.0,140,4,60,5,n173,4 +35.0,0.25,35.0,0.25,140,1,78,1,n168,4 +35.25,0.25,35.25,0.25,141,1,79,1,n169,4 +35.5,0.25,35.5,0.25,142,1,78,1,n170,4 +35.75,0.25,35.75,0.25,143,1,79,1,n171,4 +36.0,1.0,36.0,1.0,144,4,65,5,n182,4 +36.0,0.25,36.0,0.25,144,1,80,1,n174,4 +36.25,0.25,36.25,0.25,145,1,81,1,n175,4 +36.5,0.25,36.5,0.25,146,1,84,1,n176,4 +36.75,0.25,36.75,0.25,147,1,83,1,n177,4 +37.0,1.0,37.0,1.0,148,4,60,5,n183,4 +37.0,0.25,37.0,0.25,148,1,86,1,n178,4 +37.25,0.25,37.25,0.25,149,1,84,1,n179,4 +37.5,0.25,37.5,0.25,150,1,83,1,n180,4 +37.75,0.25,37.75,0.25,151,1,81,1,n181,4 +38.0,1.0,38.0,1.0,152,4,60,5,n192,4 +38.0,1.0,38.0,1.0,152,4,64,5,n193,4 +38.0,0.25,38.0,0.25,152,1,81,1,n184,4 +38.25,0.25,38.25,0.25,153,1,79,1,n185,4 +38.5,0.25,38.5,0.25,154,1,88,1,n186,4 +38.75,0.25,38.75,0.25,155,1,86,1,n187,4 +39.0,0.25,39.0,0.25,156,1,84,1,n188,4 +39.25,0.25,39.25,0.25,157,1,83,1,n189,4 +39.5,0.25,39.5,0.25,158,1,81,1,n190,4 +39.75,0.25,39.75,0.25,159,1,61,5,n195,4 +39.75,0.25,39.75,0.25,159,1,79,1,n191,4 +40.0,1.0,40.0,1.0,160,4,62,5,n204,4 +40.0,0.25,40.0,0.25,160,1,79,1,n196,4 +40.25,0.25,40.25,0.25,161,1,77,1,n197,4 +40.5,0.25,40.5,0.25,162,1,86,1,n198,4 +40.75,0.25,40.75,0.25,163,1,84,1,n199,4 +41.0,0.25,41.0,0.25,164,1,83,1,n200,4 +41.25,0.25,41.25,0.25,165,1,81,1,n201,4 +41.5,0.25,41.5,0.25,166,1,79,1,n202,4 +41.75,0.25,41.75,0.25,167,1,59,5,n206,4 +41.75,0.25,41.75,0.25,167,1,77,1,n203,4 +42.0,1.0,42.0,1.0,168,4,60,5,n215,4 +42.0,0.25,42.0,0.25,168,1,77,1,n207,4 +42.25,0.25,42.25,0.25,169,1,76,1,n208,4 +42.5,0.25,42.5,0.25,170,1,84,1,n209,4 +42.75,0.25,42.75,0.25,171,1,83,1,n210,4 +43.0,0.25,43.0,0.25,172,1,81,1,n211,4 +43.25,0.25,43.25,0.25,173,1,79,1,n212,4 +43.5,0.25,43.5,0.25,174,1,77,1,n213,4 +43.75,0.25,43.75,0.25,175,1,57,5,n217,4 +43.75,0.25,43.75,0.25,175,1,76,1,n214,4 +44.0,1.0,44.0,1.0,176,4,53,5,n222,4 +44.0,0.5,44.0,0.5,176,2,74,1,n218,4 +44.5,0.5,44.5,0.5,178,2,81,1,n219,4 +45.0,1.0,45.0,1.0,180,4,55,5,n223,4 +45.0,0.5,45.0,0.5,180,2,79,1,n220,4 +45.5,0.5,45.5,0.5,182,2,71,1,n221,4 +46.0,1.0,46.0,1.0,184,4,60,5,n226,4 +46.0,1.0,46.0,1.0,184,4,72,1,n224,4 +47.0,1.0,47.0,1.0,188,4,48,5,n227,4 diff --git a/tests/data/parangonada/mozart_k265_var1/ppart.csv b/tests/data/parangonada/mozart_k265_var1/ppart.csv new file mode 100644 index 00000000..ed449c78 --- /dev/null +++ b/tests/data/parangonada/mozart_k265_var1/ppart.csv @@ -0,0 +1,220 @@ +onset_sec,duration_sec,pitch,velocity,track,channel,id +0.7114583,0.06666667,48,70,0,0,n0 +0.72083336,0.084375,74,72,0,0,n1 +0.853125,0.07604167,72,53,0,0,n2 +0.95208335,0.06875,71,54,0,0,n3 +1.0510417,0.092708334,72,48,0,0,n4 +1.16875,0.039583333,60,67,0,0,n5 +1.175,0.047916666,71,61,0,0,n6 +1.284375,0.053125,72,44,0,0,n7 +1.3708333,0.09791667,71,65,0,0,n8 +1.4791666,0.072916664,72,61,0,0,n9 +1.6052083,0.07604167,64,75,0,0,n10 +1.6208333,0.084375,81,55,0,0,n11 +1.753125,0.071875,79,62,0,0,n12 +1.859375,0.047916666,78,52,0,0,n13 +1.971875,0.058333334,79,77,0,0,n14 +2.0666666,0.059375,78,60,0,0,n15 +2.0854166,0.042708334,60,49,0,0,n16 +2.1875,0.03125,79,52,0,0,n17 +2.2708333,0.057291668,78,64,0,0,n18 +2.3697917,0.04375,79,54,0,0,n19 +2.4916666,0.06458333,80,61,0,0,n20 +2.5166667,0.042708334,65,61,0,0,n21 +2.5989583,0.109375,81,61,0,0,n22 +2.7197917,0.115625,84,62,0,0,n23 +2.8354166,0.077083334,83,51,0,0,n24 +2.9239583,0.1375,86,61,0,0,n25 +2.9541667,0.05625,60,45,0,0,n26 +3.0614583,0.088541664,84,55,0,0,n27 +3.159375,0.0625,83,64,0,0,n28 +3.26875,0.025,81,52,0,0,n29 +3.4114583,0.853125,60,42,0,0,n30 +3.4166667,0.84791666,81,68,0,0,n31 +3.428125,0.8364583,64,59,0,0,n32 +3.5552084,0.709375,79,62,0,0,n33 +3.678125,0.5864583,88,69,0,0,n34 +3.790625,0.47395834,86,74,0,0,n35 +3.9145834,0.35,84,62,0,0,n36 +4.0052085,0.259375,83,64,0,0,n37 +4.096875,0.16770834,81,59,0,0,n38 +4.21875,0.045833334,61,60,0,0,n39 +4.227083,0.0375,79,58,0,0,n40 +4.3614583,0.078125,79,63,0,0,n41 +4.378125,0.7864583,62,79,0,0,n42 +4.467708,0.09583333,77,87,0,0,n43 +4.59375,0.5708333,86,80,0,0,n44 +4.719792,0.44479167,84,70,0,0,n45 +4.8302083,0.334375,83,67,0,0,n46 +4.9270835,0.2375,81,72,0,0,n47 +5.039583,0.125,79,61,0,0,n48 +5.1447916,0.022916667,77,68,0,0,n49 +5.15,0.038541667,59,51,0,0,n50 +5.272917,0.08541667,77,79,0,0,n51 +5.311458,0.6322917,60,64,0,0,n52 +5.391667,0.0875,76,72,0,0,n53 +5.523958,0.41979167,84,69,0,0,n54 +5.632292,0.31145832,83,68,0,0,n55 +5.7385416,0.20520833,81,61,0,0,n56 +5.829167,0.114583336,79,70,0,0,n57 +5.920833,0.071875,77,82,0,0,n58 +6.034375,0.063541666,57,65,0,0,n59 +6.0510416,0.07604167,76,61,0,0,n60 +6.16875,0.26458332,74,75,0,0,n61 +6.227083,0.25104168,53,63,0,0,n62 +6.4552083,0.26770833,81,70,0,0,n63 +6.6833334,0.20208333,55,60,0,0,n64 +6.6875,0.20208333,79,77,0,0,n65 +6.9197917,0.215625,71,69,0,0,n66 +7.1854167,1.0083333,72,51,0,0,n67 +7.1989584,0.121875,60,49,0,0,n68 +7.74375,0.45,48,42,0,0,n69 +8.25,0.032291666,64,63,0,0,n70 +8.258333,0.057291668,81,53,0,0,n71 +8.345834,0.07395833,79,61,0,0,n72 +8.464583,0.05625,78,67,0,0,n73 +8.602083,0.041666668,79,53,0,0,n74 +8.70625,0.446875,78,52,0,0,n75 +8.710417,0.44270834,55,54,0,0,n76 +8.811459,0.34166667,79,45,0,0,n77 +8.921875,0.23125,81,64,0,0,n78 +9.034375,0.11875,79,26,0,0,n79 +9.172916,0.0625,62,68,0,0,n80 +9.179167,0.10729167,79,73,0,0,n81 +9.303125,0.060416665,77,55,0,0,n82 +9.377084,0.07083333,76,72,0,0,n83 +9.50625,0.05625,77,54,0,0,n84 +9.589583,0.375,76,65,0,0,n85 +9.641666,0.32291666,55,49,0,0,n86 +9.713542,0.25104168,77,52,0,0,n87 +9.807292,0.15729167,79,63,0,0,n88 +9.895833,0.06875,77,52,0,0,n89 +10.051042,0.123958334,77,65,0,0,n90 +10.067708,0.05625,60,65,0,0,n91 +10.169791,0.1,76,67,0,0,n92 +10.28125,0.08958333,75,64,0,0,n93 +10.404166,0.07083333,76,55,0,0,n94 +10.505208,0.11145833,55,60,0,0,n95 +10.510417,0.096875,75,49,0,0,n96 +10.630208,0.0625,76,58,0,0,n97 +10.742708,0.098958336,77,65,0,0,n98 +10.827084,0.03125,76,52,0,0,n99 +10.965625,0.083333336,76,78,0,0,n100 +10.979167,0.078125,65,63,0,0,n101 +11.089583,0.067708336,74,58,0,0,n102 +11.203125,0.065625,73,55,0,0,n103 +11.322917,0.041666668,74,48,0,0,n104 +11.414583,0.38020834,73,55,0,0,n105 +11.451041,0.34375,55,49,0,0,n106 +11.557292,0.2375,74,39,0,0,n107 +11.626041,0.16875,76,55,0,0,n108 +11.701041,0.09375,74,61,0,0,n109 +11.875,0.87916666,64,60,0,0,n110 +11.88125,0.87291664,55,53,0,0,n111 +11.895833,0.077083334,81,67,0,0,n112 +12.016666,0.05625,79,66,0,0,n113 +12.128125,0.62604165,78,52,0,0,n114 +12.219791,0.534375,79,69,0,0,n115 +12.344791,0.409375,88,77,0,0,n116 +12.461458,0.29270834,84,58,0,0,n117 +12.570833,0.18333334,81,66,0,0,n118 +12.655209,0.098958336,79,64,0,0,n119 +12.816667,0.9270833,55,54,0,0,n120 +12.826041,0.91770834,62,61,0,0,n121 +12.828125,0.08125,79,75,0,0,n122 +12.942708,0.079166666,77,73,0,0,n123 +13.070833,0.06875,76,53,0,0,n124 +13.169791,0.57395834,77,77,0,0,n125 +13.29375,0.45,86,79,0,0,n126 +13.420834,0.32291666,83,59,0,0,n127 +13.529166,0.21458334,79,64,0,0,n128 +13.605208,0.13854167,77,62,0,0,n129 +13.765625,0.0875,77,64,0,0,n130 +13.778125,0.95625,55,45,0,0,n131 +13.780209,0.95416665,60,42,0,0,n132 +13.898958,0.053125,76,52,0,0,n133 +14.009375,0.725,75,61,0,0,n134 +14.135417,0.5989583,76,58,0,0,n135 +14.235416,0.49895832,84,67,0,0,n136 +14.429167,0.30520833,81,23,0,0,n137 +14.43125,0.303125,79,31,0,0,n138 +14.479167,0.25520834,77,55,0,0,n139 +14.572917,0.16145833,60,52,0,0,n140 +14.590625,0.21875,76,62,0,0,n141 +14.771875,0.74583334,55,45,0,0,n142 +14.789583,0.53229165,64,44,0,0,n143 +14.811459,0.540625,79,68,0,0,n144 +15.34375,0.14791666,60,43,0,0,n145 +15.346875,0.175,76,47,0,0,n146 +15.5125,0.63125,59,51,0,0,n147 +15.514584,0.62916666,74,44,0,0,n148 +16.234375,0.079166666,48,61,0,0,n149 +16.239584,0.08541667,74,58,0,0,n150 +16.375,0.0375,72,39,0,0,n151 +16.458334,0.058333334,71,54,0,0,n152 +16.557291,0.10208333,72,55,0,0,n153 +16.68125,0.05625,60,59,0,0,n154 +16.69375,0.072916664,71,48,0,0,n155 +16.84375,0.045833334,72,43,0,0,n156 +16.932291,0.0625,71,59,0,0,n157 +17.036459,0.022916667,72,49,0,0,n158 +17.121876,0.065625,64,59,0,0,n159 +17.145834,0.072916664,81,25,0,0,n160 +17.266666,0.07395833,79,63,0,0,n161 +17.36875,0.0625,78,58,0,0,n162 +17.502083,0.025,79,42,0,0,n163 +17.601042,0.055208333,78,54,0,0,n164 +17.604166,0.063541666,60,47,0,0,n165 +17.708334,0.03125,79,44,0,0,n166 +17.807291,0.05625,78,55,0,0,n167 +17.944792,0.0125,79,32,0,0,n168 +18.011457,0.09166667,80,60,0,0,n169 +18.039583,0.063541666,65,50,0,0,n170 +18.138542,0.10729167,81,64,0,0,n171 +18.244791,0.114583336,84,60,0,0,n172 +18.373959,0.079166666,83,44,0,0,n173 +18.459375,0.13020833,86,61,0,0,n174 +18.476042,0.088541664,60,46,0,0,n175 +18.615625,0.08020833,84,42,0,0,n176 +18.701042,0.083333336,83,55,0,0,n177 +18.786459,0.033333335,81,58,0,0,n178 +18.952084,0.79270834,60,48,0,0,n179 +18.959375,0.78541666,64,55,0,0,n180 +18.959375,0.053125,81,55,0,0,n181 +19.052084,0.6927083,79,67,0,0,n182 +19.1875,0.5572917,88,66,0,0,n183 +19.304167,0.440625,86,63,0,0,n184 +19.408333,0.33645833,84,71,0,0,n185 +19.501041,0.24375,83,68,0,0,n186 +19.596874,0.14791666,81,60,0,0,n187 +19.729166,0.047916666,61,58,0,0,n188 +19.7375,0.030208332,79,71,0,0,n189 +19.859375,0.09583333,79,77,0,0,n190 +19.882292,0.7625,62,74,0,0,n191 +19.984375,0.108333334,77,84,0,0,n192 +20.096874,0.54791665,86,75,0,0,n193 +20.217709,0.42708334,84,62,0,0,n194 +20.31875,0.32604167,83,71,0,0,n195 +20.433332,0.21145834,81,45,0,0,n196 +20.498959,0.14583333,79,70,0,0,n197 +20.635416,0.036458332,77,81,0,0,n198 +20.666666,0.0375,59,47,0,0,n199 +20.779167,0.0875,77,77,0,0,n200 +20.813541,0.73020834,60,69,0,0,n201 +20.888542,0.65520835,76,77,0,0,n202 +21.016666,0.52708334,84,68,0,0,n203 +21.120832,0.42291668,83,62,0,0,n204 +21.228125,0.315625,81,59,0,0,n205 +21.314583,0.22916667,79,71,0,0,n206 +21.413542,0.13020833,77,72,0,0,n207 +21.523958,0.083333336,76,76,0,0,n208 +21.536459,0.11666667,57,61,0,0,n209 +21.6625,0.28229168,74,64,0,0,n210 +21.722918,0.26979166,53,72,0,0,n211 +21.941668,0.42916667,81,68,0,0,n212 +22.173958,0.27916667,55,70,0,0,n213 +22.183332,0.25729167,79,75,0,0,n214 +22.4375,0.21666667,71,58,0,0,n215 +22.701042,1.16875,72,48,0,0,n216 +22.728125,0.21875,60,49,0,0,n217 +23.326042,0.596875,48,50,0,0,n218 diff --git a/tests/data/parangonada/mozart_k265_var1/zalign.csv b/tests/data/parangonada/mozart_k265_var1/zalign.csv new file mode 100644 index 00000000..078300e2 --- /dev/null +++ b/tests/data/parangonada/mozart_k265_var1/zalign.csv @@ -0,0 +1,220 @@ +idx,matchtype,partid,ppartid +0,0,n9,n0 +1,0,n1,n1 +2,0,n2,n2 +3,0,n3,n3 +4,0,n4,n4 +5,0,n10,n5 +6,0,n5,n6 +7,0,n6,n7 +8,0,n7,n8 +9,0,n8,n9 +10,0,n19,n10 +11,0,n11,n11 +12,0,n12,n12 +13,0,n13,n13 +14,0,n14,n14 +15,0,n15,n15 +16,0,n20,n16 +17,0,n16,n17 +18,0,n17,n18 +19,0,n18,n19 +20,0,n21,n20 +21,0,n29,n21 +22,0,n22,n22 +23,0,n23,n23 +24,0,n24,n24 +25,0,n25,n25 +26,0,n30,n26 +27,0,n26,n27 +28,0,n27,n28 +29,0,n28,n29 +30,0,n39,n30 +31,0,n31,n31 +32,0,n40,n32 +33,0,n32,n33 +34,0,n33,n34 +35,0,n34,n35 +36,0,n35,n36 +37,0,n36,n37 +38,0,n37,n38 +39,0,n42,n39 +40,0,n38,n40 +41,0,n43,n41 +42,0,n51,n42 +43,0,n44,n43 +44,0,n45,n44 +45,0,n46,n45 +46,0,n47,n46 +47,0,n48,n47 +48,0,n49,n48 +49,0,n50,n49 +50,0,n53,n50 +51,0,n54,n51 +52,0,n62,n52 +53,0,n55,n53 +54,0,n56,n54 +55,0,n57,n55 +56,0,n58,n56 +57,0,n59,n57 +58,0,n60,n58 +59,0,n64,n59 +60,0,n61,n60 +61,0,n65,n61 +62,0,n69,n62 +63,0,n66,n63 +64,0,n70,n64 +65,0,n67,n65 +66,0,n68,n66 +67,0,n71,n67 +68,0,n73,n68 +69,0,n74,n69 +70,0,n83,n70 +71,0,n75,n71 +72,0,n76,n72 +73,0,n77,n73 +74,0,n78,n74 +75,0,n79,n75 +76,0,n84,n76 +77,0,n80,n77 +78,0,n81,n78 +79,0,n82,n79 +80,0,n93,n80 +81,0,n85,n81 +82,0,n86,n82 +83,0,n87,n83 +84,0,n88,n84 +85,0,n89,n85 +86,0,n94,n86 +87,0,n90,n87 +88,0,n91,n88 +89,0,n92,n89 +90,0,n95,n90 +91,0,n103,n91 +92,0,n96,n92 +93,0,n97,n93 +94,0,n98,n94 +95,0,n104,n95 +96,0,n99,n96 +97,0,n100,n97 +98,0,n101,n98 +99,0,n102,n99 +100,0,n105,n100 +101,0,n113,n101 +102,0,n106,n102 +103,0,n107,n103 +104,0,n108,n104 +105,0,n109,n105 +106,0,n114,n106 +107,0,n110,n107 +108,0,n111,n108 +109,0,n112,n109 +110,0,n124,n110 +111,0,n123,n111 +112,0,n115,n112 +113,0,n116,n113 +114,0,n117,n114 +115,0,n118,n115 +116,0,n119,n116 +117,0,n120,n117 +118,0,n121,n118 +119,0,n122,n119 +120,0,n133,n120 +121,0,n134,n121 +122,0,n125,n122 +123,0,n126,n123 +124,0,n127,n124 +125,0,n128,n125 +126,0,n129,n126 +127,0,n130,n127 +128,0,n131,n128 +129,0,n132,n129 +130,0,n135,n130 +131,0,n143,n131 +132,0,n144,n132 +133,0,n136,n133 +134,0,n137,n134 +135,0,n138,n135 +136,0,n139,n136 +137,2,undefined,n137 +138,0,n140,n138 +139,0,n141,n139 +140,0,n146,n140 +141,0,n142,n141 +142,0,n153,n142 +143,0,n150,n143 +144,0,n147,n144 +145,0,n151,n145 +146,0,n148,n146 +147,0,n152,n147 +148,0,n149,n148 +149,0,n162,n149 +150,0,n154,n150 +151,0,n155,n151 +152,0,n156,n152 +153,0,n157,n153 +154,0,n163,n154 +155,0,n158,n155 +156,0,n159,n156 +157,0,n160,n157 +158,0,n161,n158 +159,0,n172,n159 +160,0,n164,n160 +161,0,n165,n161 +162,0,n166,n162 +163,0,n167,n163 +164,0,n168,n164 +165,0,n173,n165 +166,0,n169,n166 +167,0,n170,n167 +168,0,n171,n168 +169,0,n174,n169 +170,0,n182,n170 +171,0,n175,n171 +172,0,n176,n172 +173,0,n177,n173 +174,0,n178,n174 +175,0,n183,n175 +176,0,n179,n176 +177,0,n180,n177 +178,0,n181,n178 +179,0,n192,n179 +180,0,n193,n180 +181,0,n184,n181 +182,0,n185,n182 +183,0,n186,n183 +184,0,n187,n184 +185,0,n188,n185 +186,0,n189,n186 +187,0,n190,n187 +188,0,n195,n188 +189,0,n191,n189 +190,0,n196,n190 +191,0,n204,n191 +192,0,n197,n192 +193,0,n198,n193 +194,0,n199,n194 +195,0,n200,n195 +196,0,n201,n196 +197,0,n202,n197 +198,0,n203,n198 +199,0,n206,n199 +200,0,n207,n200 +201,0,n215,n201 +202,0,n208,n202 +203,0,n209,n203 +204,0,n210,n204 +205,0,n211,n205 +206,0,n212,n206 +207,0,n213,n207 +208,0,n214,n208 +209,0,n217,n209 +210,0,n218,n210 +211,0,n222,n211 +212,0,n219,n212 +213,0,n223,n213 +214,0,n220,n214 +215,0,n221,n215 +216,0,n224,n216 +217,0,n226,n217 +218,0,n227,n218 diff --git a/tests/data/wav/example_linear_equal_temperament_sr8000.wav b/tests/data/wav/example_linear_equal_temperament_sr8000.wav new file mode 100644 index 00000000..ee39ba96 Binary files /dev/null and b/tests/data/wav/example_linear_equal_temperament_sr8000.wav differ diff --git a/tests/test_deprecations.py b/tests/test_deprecations.py new file mode 100644 index 00000000..3130c9f1 --- /dev/null +++ b/tests/test_deprecations.py @@ -0,0 +1,43 @@ +import unittest +import warnings +import numpy as np + +from partitura.utils import deprecated_alias, deprecated_parameter + +RNG = np.random.RandomState(1984) + + +class TestDeprecations(unittest.TestCase): + @deprecated_alias(old_p1="new_p1", old_p2="new_p2") + def func_alias(self, new_p1=None, new_p2=None, **kwargs): + + crit = not ("old_p1" in kwargs or "old_p2" in kwargs) + + self.assertTrue(crit) + self.assertTrue(new_p1 is not None) + self.assertTrue(new_p2 is not None) + + @deprecated_parameter(*[f"deprecated{i}" for i in range(10)]) + def func_parameter(self, new_p1=None, new_p2=None, **kwargs): + + crit = not any([f"deprecated{i}" in kwargs for i in range(10)]) + + self.assertTrue(crit) + self.assertTrue(new_p1 is not None) + self.assertTrue(new_p2 is not None) + + def test_deprecated_alias(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + for old_p1, old_p2 in RNG.rand(10, 2): + self.func_alias(old_p1=old_p1, old_p2=old_p2) + + def test_deprecated_parameter(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + for rp in RNG.rand(10, 12): + kwargs = dict( + [("new_p1", rp[0]), ("new_p2", rp[1])] + + [(f"deprecated{i}", rp[i + 2]) for i in range(10)] + ) + self.func_parameter(**kwargs) diff --git a/tests/test_kern.py b/tests/test_kern.py index d163c104..1fb663dc 100644 --- a/tests/test_kern.py +++ b/tests/test_kern.py @@ -4,7 +4,7 @@ import unittest import partitura -from tests import KERN_TESFILES, KERN_TIES +from tests import KERN_TESTFILES, KERN_TIES from partitura.score import merge_parts from partitura.utils import ensure_notearray from partitura.io.importkern import load_kern @@ -29,7 +29,7 @@ def test_example_kern(self): ) def test_examples(self): - for fn in KERN_TESFILES: + for fn in KERN_TESTFILES: part = merge_parts(load_kern(fn)) ka = ensure_notearray(part) self.assertTrue(True == True) @@ -40,3 +40,7 @@ def test_tie_mismatch(self): part = merge_parts(load_kern(fn)) self.assertTrue(True == True) + + +# if __name__ == "__main__": +# unittest.main() diff --git a/tests/test_load_score.py b/tests/test_load_score.py index f261401e..b6297217 100644 --- a/tests/test_load_score.py +++ b/tests/test_load_score.py @@ -8,7 +8,7 @@ from tests import ( MUSICXML_IMPORT_EXPORT_TESTFILES, MEI_TESTFILES, - KERN_TESFILES, + KERN_TESTFILES, MATCH_IMPORT_EXPORT_TESTFILES, ) @@ -33,7 +33,7 @@ def test_load_score(self): for fn in ( MUSICXML_IMPORT_EXPORT_TESTFILES + MEI_TESTFILES - + KERN_TESFILES + + KERN_TESTFILES + MATCH_IMPORT_EXPORT_TESTFILES + EXAMPLE_FILES ): @@ -42,6 +42,11 @@ def test_load_score(self): def load_score(self, fn): try: score = load_score(fn) - self.assertTrue(type(score) in (Part, PartGroup, list, Score)) + self.assertTrue(isinstance(score, Score)) + + for pp in score.part_structure: + self.assertTrue(type(pp) in (Part, PartGroup)) + for pp in score.parts: + self.assertTrue(isinstance(pp, Part)) except NotSupportedFormatError: self.assertTrue(False) diff --git a/tests/test_mei.py b/tests/test_mei.py index 52cd1742..0fd58b33 100644 --- a/tests/test_mei.py +++ b/tests/test_mei.py @@ -103,12 +103,14 @@ def test_handle_layer_tuplets(self): self.assertTrue(len(part.note_array()) == 10) def test_ties1(self): - part_list = load_mei(MEI_TESTFILES[7]) - note_array = list(score.iter_parts(part_list))[0].note_array - self.assertTrue(len(note_array()) == 4) + scr = load_mei(MEI_TESTFILES[7]) + part_list = scr.parts + note_array = list(score.iter_parts(part_list))[0].note_array() + self.assertTrue(len(note_array) == 4) def test_time_signatures(self): - part_list = load_mei(MEI_TESTFILES[8]) + scr = load_mei(MEI_TESTFILES[8]) + part_list = scr.parts part0 = list(score.iter_parts(part_list))[0] time_signatures = list(part0.iter_all(score.TimeSignature)) self.assertTrue(len(time_signatures) == 3) @@ -117,7 +119,7 @@ def test_time_signatures(self): self.assertTrue(time_signatures[2].start.t == 12.5 * 16) def test_clef(self): - part_list = load_mei(MEI_TESTFILES[9]) + part_list = load_mei(MEI_TESTFILES[9]).parts # test on part 2 part2 = list(score.iter_parts(part_list))[2] clefs2 = list(part2.iter_all(score.Clef)) @@ -144,7 +146,7 @@ def test_clef(self): self.assertTrue(clefs3[1].octave_change == -1) def test_key_signature1(self): - part_list = load_mei(MEI_TESTFILES[9]) + part_list = load_mei(MEI_TESTFILES[9]).parts for part in score.iter_parts(part_list): kss = list(part.iter_all(score.KeySignature)) self.assertTrue(len(kss) == 2) @@ -152,14 +154,14 @@ def test_key_signature1(self): self.assertTrue(kss[1].fifths == 4) def test_key_signature2(self): - part_list = load_mei(MEI_TESTFILES[10]) + part_list = load_mei(MEI_TESTFILES[10]).parts for part in score.iter_parts(part_list): kss = list(part.iter_all(score.KeySignature)) self.assertTrue(len(kss) == 1) self.assertTrue(kss[0].fifths == -1) def test_grace_note(self): - part_list = load_mei(MEI_TESTFILES[10]) + part_list = load_mei(MEI_TESTFILES[10]).parts part = list(score.iter_parts(part_list))[0] grace_notes = list(part.iter_all(score.GraceNote)) self.assertTrue(len(part.note_array()) == 7) @@ -168,7 +170,7 @@ def test_grace_note(self): self.assertTrue(grace_notes[1].grace_type == "appoggiatura") def test_meter_in_scoredef(self): - part_list = load_mei(MEI_TESTFILES[11]) + part_list = load_mei(MEI_TESTFILES[11]).parts self.assertTrue(True) def test_infer_ppq(self): @@ -178,32 +180,32 @@ def test_infer_ppq(self): def test_no_ppq(self): # compare the same piece with and without ppq annotations - parts_ppq = load_mei(MEI_TESTFILES[6]) + parts_ppq = load_mei(MEI_TESTFILES[6]).parts part_ppq = list(score.iter_parts(parts_ppq))[0] note_array_ppq = part_ppq.note_array() - parts_no_ppq = load_mei(MEI_TESTFILES[12]) + parts_no_ppq = load_mei(MEI_TESTFILES[12]).parts part_no_ppq = list(score.iter_parts(parts_no_ppq))[0] note_array_no_ppq = part_no_ppq.note_array() self.assertTrue(np.array_equal(note_array_ppq, note_array_no_ppq)) def test_part_duration(self): - parts_no_ppq = load_mei(MEI_TESTFILES[14]) + parts_no_ppq = load_mei(MEI_TESTFILES[14]).parts part_no_ppq = list(score.iter_parts(parts_no_ppq))[0] note_array_no_ppq = part_no_ppq.note_array() self.assertTrue(part_no_ppq._quarter_durations[0] == 4) self.assertTrue(sorted(part_no_ppq._points)[-1].t == 12) def test_part_duration2(self): - parts_no_ppq = load_mei(MEI_TESTFILES[15]) + parts_no_ppq = load_mei(MEI_TESTFILES[15]).parts part_no_ppq = list(score.iter_parts(parts_no_ppq))[0] note_array_no_ppq = part_no_ppq.note_array() self.assertTrue(part_no_ppq._quarter_durations[0] == 8) self.assertTrue(sorted(part_no_ppq._points)[-1].t == 22) def test_barline(self): - parts = load_mei(MEI_TESTFILES[16]) + parts = load_mei(MEI_TESTFILES[16]).parts part = list(score.iter_parts(parts))[0] barlines = list(part.iter_all(score.Barline)) expected_barlines_times = [0, 8, 8, 16, 20, 24, 28] @@ -220,7 +222,7 @@ def test_barline(self): self.assertTrue([bl.style for bl in barlines] == expected_barlines_style) def test_repetition1(self): - parts = load_mei(MEI_TESTFILES[16]) + parts = load_mei(MEI_TESTFILES[16]).parts part = list(score.iter_parts(parts))[0] repetitions = list(part.iter_all(score.Repeat)) expected_repeat_starts = [0, 8] @@ -229,7 +231,7 @@ def test_repetition1(self): self.assertTrue([rp.end.t for rp in repetitions] == expected_repeat_ends) def test_repetition2(self): - parts = load_mei(MEI_TESTFILES[17]) + parts = load_mei(MEI_TESTFILES[17]).parts part = list(score.iter_parts(parts))[0] fine_els = list(part.iter_all(score.Fine)) self.assertTrue(len(fine_els) == 1) @@ -244,13 +246,13 @@ def test_repetition2(self): # self.assertTrue(False) def test_parse_mei_example(self): - part_list = load_mei(EXAMPLE_MEI) + part_list = load_mei(EXAMPLE_MEI).parts self.assertTrue(True) def test_parse_mei(self): # check if all test files load correctly for mei in MEI_TESTFILES[4:]: - part_list = load_mei(mei) + part_list = load_mei(mei).parts self.assertTrue(True) # def test_parse_all(self): diff --git a/tests/test_midi_export.py b/tests/test_midi_export.py index 7f591511..1b088058 100644 --- a/tests/test_midi_export.py +++ b/tests/test_midi_export.py @@ -5,13 +5,18 @@ import unittest from tempfile import TemporaryFile import mido +import numpy as np -from partitura import save_score_midi +from partitura import save_score_midi, save_performance_midi, load_performance_midi from partitura.utils import partition import partitura.score as score +from partitura.performance import PerformedPart, Performance + LOGGER = logging.getLogger(__name__) +RNG = np.random.RandomState(1984) + def get_track_voice_numbers(mid): counter = Counter() @@ -337,6 +342,42 @@ def test_midi_export_anacrusis(self): assert t == 3, f"Incorrect time of first note on: {t} (should be 3)" break + def test_midi_export_score(self): + + part = score.Part("id") + # 1 div is 1 quarter + part.set_quarter_duration(0, 1) + # 4/4 at t=0 + part.add(score.TimeSignature(4, 4), 0) + + # ANACRUSIS + # quarter note from t=0 to t=1 + part.add(score.Note("c", 4), 0, 1) + # incomplete measure from t=0 to t=1 + part.add(score.Measure(), 0, 1) + + # whole note from t=1 to t=5 + part.add(score.Note("c", 4), 1, 5) + # add missing measures + score.add_measures(part) + + scr = score.Score(part) + mid = export_and_read(scr, anacrusis_behavior="shift") + t = 0 + for msg in mid.tracks[0]: + t += msg.time + if msg.type == "note_on": + self.assertEqual(t, 0) + break + + mid = export_and_read(scr, anacrusis_behavior="pad_bar") + t = 0 + for msg in mid.tracks[0]: + t += msg.time + if msg.type == "note_on": + self.assertEqual(t, 3) + break + def n_items_per_part_voice(pg, cls): n_items = [] @@ -344,3 +385,94 @@ def n_items_per_part_voice(pg, cls): n = sum(1 for _ in part.iter_all(cls)) n_items.extend([n] * len(set(n.voice for n in part.notes_tied))) return n_items + + +def export_and_read_performance(perf_info, **kwargs): + + with TemporaryFile(suffix=".mid") as f: + save_performance_midi( + performance_data=perf_info, + out=f, + **kwargs, + ) + f.flush() + f.seek(0) + return mido.MidiFile(file=f) + + +class TestExportPerformanceMIDI(unittest.TestCase): + def _export_and_read(self, perf_info, **kwargs): + return export_and_read_performance(perf_info, **kwargs) + + def test_save_single_track(self): + + ppart = generate_random_performance(n_tracks=1) + + note_array = ppart.note_array() + + tracks = note_array["track"] + + self.assertTrue(all(tracks == 0)) + + mf_from_ppart = self._export_and_read(ppart) + + self.assertEqual(ppart.num_tracks, len(mf_from_ppart.tracks)) + + def test_save_multiple_track(self): + n_tracks = RNG.randint(2, 10, 10) + for nt in n_tracks: + + ppart = generate_random_performance(n_notes=10 * nt, n_tracks=nt) + + performance = Performance(performedparts=ppart) + mf_from_perf = self._export_and_read(performance) + self.assertEqual(performance.num_tracks, len(mf_from_perf.tracks)) + + +def generate_random_performance(n_notes=100, beat_period=0.5, n_tracks=3): + + note_array = np.empty( + (n_notes), + dtype=[ + ("onset_sec", "f4"), + ("duration_sec", "f4"), + ("pitch", "i4"), + ("velocity", "i4"), + ("track", "i4"), + ("channel", "i4"), + ("id", "U256"), + ], + ) + + note_array["pitch"] = RNG.randint(0, 128, n_notes) + + ioi = RNG.rand(n_notes - 1) * beat_period + + note_array["onset_sec"] = np.r_[0, np.cumsum(ioi)] + + note_array["duration_sec"] = np.clip( + RNG.rand(n_notes) * 2 * beat_period, + a_min=0.3, + a_max=2, + ) + + note_array["velocity"] = RNG.randint(54, 90, n_notes) + + note_array["channel"] *= 0 + + note_array["id"] = np.array([f"n{i}" for i in range(n_notes)]) + + track_idxs = np.arange(n_notes) + RNG.shuffle(track_idxs) + + track_length = int(np.floor(n_notes / n_tracks)) + + for i in range(n_tracks): + if i < n_tracks - 1: + idx = track_idxs[i * track_length : (i + 1) * track_length] + else: + idx = track_idxs[i * track_length :] + note_array["track"][idx] = i + + performed_part = PerformedPart.from_note_array(note_array) + return performed_part diff --git a/tests/test_midi_import.py b/tests/test_midi_import.py index 622e5175..a4de9dff 100644 --- a/tests/test_midi_import.py +++ b/tests/test_midi_import.py @@ -145,7 +145,8 @@ def test_tuplets(self): mid, actual, normal = example_gen_func() with NamedTemporaryFile(suffix=".mid", delete=False) as fh: mid.save(fh.name) - part = load_score_midi(fh.name, part_voice_assign_mode=0) + scr = load_score_midi(fh.name, part_voice_assign_mode=0) + part = scr[0] notes = part.notes if len(actual) != len(normal): @@ -208,7 +209,9 @@ def test_midi_import_mode_0(self): self.assertEqual(n_ch_notes, n_voice_notes, msg) def test_midi_import_mode_1(self): - parts = load_score_midi(self.tmpfile.name, part_voice_assign_mode=1) + scr = load_score_midi(self.tmpfile.name, part_voice_assign_mode=1) + + parts = scr.part_structure by_track = partition(itemgetter(0), self.notes_per_tr_ch.keys()) msg = ( "Number of partgroups {} does not equal number of tracks {} while " @@ -239,7 +242,9 @@ def test_midi_import_mode_1(self): self.assertEqual(notes_in_part, notes_in_track) def test_midi_import_mode_2(self): - part = load_score_midi(self.tmpfile.name, part_voice_assign_mode=2) + scr = load_score_midi(self.tmpfile.name, part_voice_assign_mode=2) + self.assertTrue(len(scr) == 1) + part = scr.part_structure[0] msg = "{} should be a Part instance but it is not".format(part) self.assertTrue(isinstance(part, score.Part), msg) by_track = partition(itemgetter(0), self.notes_per_tr_ch.keys()) @@ -278,7 +283,9 @@ def test_midi_import_mode_3(self): self.assertEqual(n_track_notes, n_part_notes, msg) def test_midi_import_mode_4(self): - part = load_score_midi(self.tmpfile.name, part_voice_assign_mode=4) + scr = load_score_midi(self.tmpfile.name, part_voice_assign_mode=4) + # this shold be a part + part = scr.part_structure[0] msg = "{} should be a Part instance but it is not".format(part) self.assertTrue(isinstance(part, score.Part), msg) midi_notes = sum(self.notes_per_tr_ch.values()) diff --git a/tests/test_note_array.py b/tests/test_note_array.py index 8f56765e..c66fc336 100644 --- a/tests/test_note_array.py +++ b/tests/test_note_array.py @@ -11,7 +11,7 @@ from partitura.utils.music import note_array_from_part, ensure_notearray import numpy as np -from tests import NOTE_ARRAY_TESTFILES, KERN_TESFILES +from tests import NOTE_ARRAY_TESTFILES, KERN_TESTFILES class TestNoteArray(unittest.TestCase): @@ -86,7 +86,8 @@ def test_notearray_ts_beats(self): def test_ensure_na_different_divs(self): # check if divs are correctly rescaled when producing a note array from # parts with different divs values - parts = list(score.iter_parts(load_kern(KERN_TESFILES[7]))) + # parts = list(score.iter_parts(load_kern(KERN_TESTFILES[7]))) + parts = load_kern(KERN_TESTFILES[7]).parts # note_arrays = [p.note_array(include_divs_per_quarter= True) for p in parts] merged_note_array = ensure_notearray(parts) for note in merged_note_array[-4:]: diff --git a/tests/test_parangonada.py b/tests/test_parangonada.py index 4b1d6bb8..6c32ad92 100644 --- a/tests/test_parangonada.py +++ b/tests/test_parangonada.py @@ -9,13 +9,23 @@ import tempfile import os +from partitura import load_score, load_match from partitura.io.exportparangonada import ( - save_alignment_for_parangonada, - load_alignment_from_parangonada, + save_parangonada_alignment, save_alignment_for_ASAP, + save_parangonada_csv, +) + +from partitura.io.importparangonada import ( + load_parangonada_alignment, load_alignment_from_ASAP, + _load_csv, + load_parangonada_csv, ) + +from tests import MOZART_VARIATION_FILES + LOGGER = logging.getLogger(__name__) test_alignment = [ @@ -25,6 +35,23 @@ ] +_performance, _alignment = load_match(filename=MOZART_VARIATION_FILES["match"]) +MOZART_VARIATION_DATA = dict( + score=load_score(MOZART_VARIATION_FILES["musicxml"]), + performance=_performance, + alignment=_alignment, + parangonada_align=_load_csv( + MOZART_VARIATION_FILES["parangonada_align"], + ), + parangonada_zalign=_load_csv( + MOZART_VARIATION_FILES["parangonada_zalign"], + ), + parangonada_feature=_load_csv(MOZART_VARIATION_FILES["parangonada_feature"]), + parangonada_ppart=_load_csv(MOZART_VARIATION_FILES["parangonada_ppart"]), + parangonada_spart=_load_csv(MOZART_VARIATION_FILES["parangonada_spart"]), +) + + class Ppart: def __init__(self): self.notes = [ @@ -44,6 +71,19 @@ def __init__(self): }, ] + # Make dummy Ppart iterable + def __getitem__(self, index): + return self + + def __iter__(self): + return self + + def __next__(self): + return self + + def __len__(self): + return 1 + test_ppart = Ppart() @@ -56,10 +96,10 @@ class TestIO(unittest.TestCase): def test_csv_import_export(self): with tempfile.TemporaryDirectory() as tmpdirname: - save_alignment_for_parangonada( - os.path.join(tmpdirname, "align.csv"), test_alignment + save_parangonada_alignment( + out=os.path.join(tmpdirname, "align.csv"), alignment=test_alignment ) - import_alignment = load_alignment_from_parangonada( + import_alignment = load_parangonada_alignment( os.path.join(tmpdirname, "align.csv") ) equal = test_alignment == import_alignment @@ -68,7 +108,9 @@ def test_csv_import_export(self): def test_tsv_import_export(self): with tempfile.TemporaryDirectory() as tmpdirname: save_alignment_for_ASAP( - os.path.join(tmpdirname, "align.tsv"), test_ppart, test_alignment + out=os.path.join(tmpdirname, "align.tsv"), + performance_data=test_ppart, + alignment=test_alignment, ) import_alignment = load_alignment_from_ASAP( os.path.join(tmpdirname, "align.tsv") @@ -76,6 +118,21 @@ def test_tsv_import_export(self): equal = test_alignment == import_alignment self.assertTrue(equal) + def test_save_parangonada_csv(self): + + with tempfile.TemporaryDirectory() as tmpdirname: + + save_parangonada_csv( + alignment=MOZART_VARIATION_DATA["alignment"], + performance_data=MOZART_VARIATION_DATA["performance"], + score_data=MOZART_VARIATION_DATA["score"], + outdir=tmpdirname, + ) + + performance, alignment, _, _ = load_parangonada_csv(tmpdirname) + + self.assertTrue(alignment == MOZART_VARIATION_DATA["alignment"]) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_performance.py b/tests/test_performance.py new file mode 100644 index 00000000..f53609c0 --- /dev/null +++ b/tests/test_performance.py @@ -0,0 +1,140 @@ +""" + +This file contains test functions for the `performance` module + +""" +import unittest +import numpy as np + +from partitura.performance import PerformedPart, Performance + +RNG = np.random.RandomState(1984) + + +class TestPerformance(unittest.TestCase): + def test_init_performance(self): + + note_arrays = [generate_random_note_array(100) for i in range(3)] + + performedparts = [PerformedPart.from_note_array(na) for na in note_arrays] + + perf_from_ppart = Performance( + id="", + performedparts=performedparts[0], + ) + + self.assertTrue(isinstance(perf_from_ppart, Performance)) + perf_from_ppart_list = Performance(id="", performedparts=performedparts) + self.assertTrue(isinstance(perf_from_ppart_list, Performance)) + + try: + + class NotAPerformance: + pass + + other = NotAPerformance() + perf_from_other = Performance(id="", performedparts=other) + + except ValueError: + # assert that other is not a performed part + self.assertTrue(not isinstance(other, PerformedPart)) + + def test_num_tracks(self): + + num_parts_tracks = RNG.randint(3, 10, (10, 2)) + + for nparts, ntracks in num_parts_tracks: + # all of these arrays have tracks numbered 0-ntracks - 1 + note_arrays = [ + generate_random_note_array(100, n_tracks=ntracks) for i in range(nparts) + ] + + performedparts = [PerformedPart.from_note_array(na) for na in note_arrays] + + # test that the number of tracks within each performed part is correct + self.assertTrue(all([pp.num_tracks == ntracks for pp in performedparts])) + + performance = Performance( + id="", performedparts=performedparts, ensure_unique_tracks=False + ) + + # test whether the number of parts is correct + self.assertEqual(performance.num_tracks, nparts * ntracks) + + def test_sanitize_track_numbers(self): + + num_parts_tracks = RNG.randint(3, 10, (10, 2)) + + for nparts, ntracks in num_parts_tracks: + + note_arrays = [ + generate_random_note_array(100, n_tracks=ntracks) for i in range(nparts) + ] + + performedparts = [PerformedPart.from_note_array(na) for na in note_arrays] + + performance = Performance( + id="", performedparts=performedparts, ensure_unique_tracks=False + ) + + note_array_before = performance.note_array() + + tracks = np.unique(note_array_before["track"]) + + self.assertTrue(np.all(tracks == np.arange(ntracks))) + + # sanitize track numbers + performance.sanitize_track_numbers() + + note_array_after = performance.note_array() + + after_tracks = np.unique(note_array_after["track"]) + + self.assertTrue(np.all(after_tracks == np.arange(ntracks * nparts))) + + +def generate_random_note_array(n_notes=100, beat_period=0.5, n_tracks=3): + + note_array = np.empty( + (n_notes), + dtype=[ + ("onset_sec", "f4"), + ("duration_sec", "f4"), + ("pitch", "i4"), + ("velocity", "i4"), + ("track", "i4"), + ("channel", "i4"), + ("id", "U256"), + ], + ) + + note_array["pitch"] = RNG.randint(0, 128, n_notes) + + ioi = RNG.rand(n_notes - 1) * beat_period + + note_array["onset_sec"] = np.r_[0, np.cumsum(ioi)] + + note_array["duration_sec"] = np.clip( + RNG.rand(n_notes) * 2 * beat_period, + a_min=0.3, + a_max=2, + ) + + note_array["velocity"] = RNG.randint(54, 90, n_notes) + + note_array["channel"] *= 0 + + note_array["id"] = np.array([f"n{i}" for i in range(n_notes)]) + + track_idxs = np.arange(n_notes) + RNG.shuffle(track_idxs) + + track_length = int(np.floor(n_notes / n_tracks)) + + for i in range(n_tracks): + if i < n_tracks - 1: + idx = track_idxs[i * track_length : (i + 1) * track_length] + else: + idx = track_idxs[i * track_length :] + note_array["track"][idx] = i + return note_array diff --git a/tests/test_performance_codec.py b/tests/test_performance_codec.py index 4f9ecd7f..6c932237 100644 --- a/tests/test_performance_codec.py +++ b/tests/test_performance_codec.py @@ -13,7 +13,7 @@ class TestPerformanceCoded(unittest.TestCase): def test_encode_decode(self): for fn in MATCH_IMPORT_EXPORT_TESTFILES: - ppart, alignment, spart = load_match(fn, create_part=True) + ppart, alignment, spart = load_match(filename=fn, create_score=True) performance_array, _ = encode_performance(spart[0], ppart[0], alignment) decoded_ppart, decoded_alignment = decode_performance( diff --git a/tests/test_pianoroll.py b/tests/test_pianoroll.py index e7dc91e9..23886e77 100644 --- a/tests/test_pianoroll.py +++ b/tests/test_pianoroll.py @@ -8,7 +8,7 @@ from partitura import load_musicxml, load_score, load_kern import partitura -from tests import MUSICXML_IMPORT_EXPORT_TESTFILES, PIANOROLL_TESTFILES, KERN_TESFILES +from tests import MUSICXML_IMPORT_EXPORT_TESTFILES, PIANOROLL_TESTFILES, KERN_TESTFILES LOGGER = logging.getLogger(__name__) @@ -248,14 +248,14 @@ class TestPianorollFromScores(unittest.TestCase): def test_score_pianoroll(self): # normally call the function - parts = load_score(PIANOROLL_TESTFILES[0], ensure_list=True) + parts = load_score(PIANOROLL_TESTFILES[0]) pr0 = compute_pianoroll(parts[0]) pr1 = compute_pianoroll(parts[1]) pr2 = compute_pianoroll(parts[2]) self.assertTrue(pr0.shape != pr1.shape) self.assertTrue(pr1.shape != pr2.shape) # remove the silence - parts = load_score(PIANOROLL_TESTFILES[0], ensure_list=True) + parts = load_score(PIANOROLL_TESTFILES[0]) pr0 = compute_pianoroll( parts[0], time_unit="beat", time_div=1, remove_silence=False ) @@ -269,7 +269,7 @@ def test_score_pianoroll(self): self.assertTrue(pr1.shape == (128, 8)) self.assertTrue(pr0.shape == (128, 12)) # set a fixed end - parts = load_score(PIANOROLL_TESTFILES[0], ensure_list=True) + parts = load_score(PIANOROLL_TESTFILES[0]) pr0 = compute_pianoroll( parts[0], time_unit="beat", time_div=2, remove_silence=False ) @@ -285,7 +285,7 @@ def test_score_pianoroll(self): def test_sum_pianoroll(self): time_div = 4 - parts = load_score(PIANOROLL_TESTFILES[2], ensure_list=True) + parts = load_score(PIANOROLL_TESTFILES[2]) prs = [] for part in parts: prs.append(compute_pianoroll(part, time_unit="beat", time_div=time_div)) @@ -300,8 +300,9 @@ def test_sum_pianoroll(self): self.assertTrue(np.array_equal(clipped_pr_sum, original_pianoroll)) def test_pianoroll_length(self): - score = load_score(KERN_TESFILES[7], ensure_list=True) - parts = list(partitura.score.iter_parts(score)) + score = load_score(KERN_TESTFILES[7]) + parts = score.parts + # parts = list(partitura.score.iter_parts(score)) # set musical beat if requested for part in parts: part.use_musical_beat() diff --git a/tests/test_synth.py b/tests/test_synth.py new file mode 100644 index 00000000..ace725ee --- /dev/null +++ b/tests/test_synth.py @@ -0,0 +1,163 @@ +import unittest + +import numpy as np +from scipy.io import wavfile +import tempfile + +from partitura.utils.synth import ( + midi_pitch_to_natural_frequency, + exp_in_exp_out, + lin_in_lin_out, + additive_synthesis, + synthesize, +) + +from partitura import EXAMPLE_MUSICXML, load_score + +from partitura import save_wav + +RNG = np.random.RandomState(1984) + +from tests import WAV_TESTFILES + + +class TestMidiPitchToNaturalFrequency(unittest.TestCase): + def test_octaves(self): + # all As + midi_pitch = 12 * np.arange(10) + 9 + # frequencies + frequency = midi_pitch_to_natural_frequency(midi_pitch) + # ratio of frequencies all of them should be 2 + freq_ratios = frequency[1:] / frequency[:-1] + # make test + self.assertTrue(np.allclose(freq_ratios, 2)) + + +class TestEnvelopes(unittest.TestCase): + def test_exp_in_exp_out(self): + + num_frames = 700 + envelope = exp_in_exp_out(num_frames) + + decay_frames = num_frames // 10 + attack_frames = num_frames // 100 + + envelop_attack = envelope[:attack_frames] + + envelop_decay = envelope[-decay_frames:] + + # compute second derivative, since the function of log envelope + # since the function is exponential and the input is linear, + # the second derivative must be 0 + diff2_attack = np.diff(np.diff(np.log(envelop_attack))) + diff2_decay = np.diff(np.diff(np.log(envelop_decay))) + + self.assertTrue(np.allclose(diff2_attack, 0)) + self.assertTrue(np.allclose(diff2_decay, 0)) + + def test_lin_in_lin_out(self): + + num_frames = 700 + envelope = lin_in_lin_out(num_frames) + + decay_frames = num_frames // 10 + attack_frames = num_frames // 100 + + envelop_attack = envelope[:attack_frames] + + envelop_decay = envelope[-decay_frames:] + + # compute second derivative, since the function of envelope + # since the function is exponential and the input is linear, + # the second derivative must be 0 + diff2_attack = np.diff(np.diff(envelop_attack)) + diff2_decay = np.diff(np.diff(envelop_decay)) + + self.assertTrue(np.allclose(diff2_attack, 0)) + self.assertTrue(np.allclose(diff2_decay, 0)) + + +class TestAdditiveSynthesis(unittest.TestCase): + def constant_envelope(self, x): + return 1 + + def test_freqs(self): + + for freq in np.linspace(10, 1000, 10): + + rand = RNG.rand() + y = additive_synthesis( + freqs=np.array([freq, freq + rand * freq]), + duration=1, + samplerate=100, + envelope_fun=self.constant_envelope, + ) + + num_frames = 100 + x = np.linspace(0, 1, num_frames) + y_target = np.sin(2 * np.pi * freq * x) + np.sin( + 2 * np.pi * (freq + rand * freq) * x + ) + y_target *= 0.5 + + self.assertTrue(np.allclose(y, y_target)) + + def test_size(self): + + samplerate = np.arange(1, 100, 10) * 10 + duration = np.arange(10) + + for sr in samplerate: + for dur in duration: + + expected_length = dur * sr + + y = additive_synthesis(freqs=440, duration=dur, samplerate=sr) + + self.assertTrue(len(y) == expected_length) + + +class TestSynthExport(unittest.TestCase): + + score = load_score(EXAMPLE_MUSICXML) + + def test_synthesize(self): + + for fn in WAV_TESTFILES: + + sr, original_audio = wavfile.read(fn) + + target_audio = synthesize( + note_info=self.score, + samplerate=sr, + envelope_fun="linear", + tuning="equal_temperament", + bpm=60, + ) + + # The test seems to work on MacOS, but not on Linux... + self.assertTrue(len(target_audio) == len(original_audio)) + + def test_export(self): + + for fn in WAV_TESTFILES: + sr, original_audio = wavfile.read(fn) + with tempfile.TemporaryFile(suffix=".mid") as filename: + + save_wav( + input_data=self.score, + out=filename, + samplerate=sr, + tuning="equal_temperament", + bpm=60, + envelope_fun="linear", + ) + + sr_rec, rec_audio = wavfile.read(filename) + + self.assertTrue(sr_rec == sr) + self.assertTrue(len(rec_audio) == len(original_audio)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_time_estimation.py b/tests/test_time_estimation.py new file mode 100644 index 00000000..52121bcd --- /dev/null +++ b/tests/test_time_estimation.py @@ -0,0 +1,74 @@ +import numpy as np + +import unittest +from tempfile import TemporaryFile + +from tests import VOSA_TESTFILES + +from partitura import load_musicxml +from partitura.musicanalysis import estimate_time +import partitura + + +class TestTempoMeterBeats(unittest.TestCase): + """ + Test tempo, meter numerator, and beat estimation. + """ + + score = load_musicxml(VOSA_TESTFILES[0]) + tempometerbeats = estimate_time(score) + + def testtempo(self): + + some_performance_notes = np.array([ + ( 5.7025 , 2.4375 , 40, 22, 1, 0, 'n1'), + ( 5.70375, 2.43625, 64, 54, 1, 0, 'n2'), + ( 5.77625, 2.36375, 56, 26, 1, 0, 'n3'), + ( 6.4325 , 1.7075 , 47, 20, 1, 0, 'n4'), + ( 6.9725 , 1.1675 , 63, 52, 1, 0, 'n6'), + ( 7.47625, 0.66375, 64, 59, 1, 0, 'n8'), + # + ( 8.03375, 4.20625, 66, 58, 1, 0, 'n11'), + ( 8.06875, 2.04125, 35, 30, 1, 0, 'n12'), + ( 8.06875, 4.17125, 63, 41, 1, 0, 'n13'), + ( 8.09 , 0.625 , 57, 32, 1, 0, 'n14'), + ( 8.70375, 1.40625, 47, 31, 1, 0, 'n15'), + ( 9.2075 , 0.9025 , 57, 40, 1, 0, 'n17'), + ( 9.66625, 0.4825 , 47, 30, 1, 0, 'n18'), + # + (10.1375 , 2.1025 , 57, 39, 1, 0, 'n20'), + (10.14 , 2.1 , 35, 30, 1, 0, 'n21'), + (10.63 , 1.61 , 68, 57, 1, 0, 'n22'), + (11.09625, 1.14375, 68, 63, 1, 0, 'n25'), + (11.56 , 0.68 , 66, 65, 1, 0, 'n28'), + # + (12.15875, 4.14125, 68, 61, 1, 0, 'n31'), + (12.18125, 1.98875, 40, 29, 1, 0, 'n32'), + (12.1875 , 2.0675 , 64, 48, 1, 0, 'n33'), + (12.1975 , 1.9725 , 56, 33, 1, 0, 'n34'), + (12.82 , 1.35 , 47, 27, 1, 0, 'n35'), + (13.30625, 0.86375, 56, 36, 1, 0, 'n37'), + (13.8325 , 0.47125, 47, 25, 1, 0, 'n38') + ], + dtype=[('onset_sec', '