cuthbertLab / music21

music21 is a Toolkit for Computational Musicology
https://www.music21.org/
Other
2.09k stars 397 forks source link

**harm parser integration in music21 #467

Closed napulen closed 4 years ago

napulen commented 4 years ago

I have made a parser of the **harm syntax for Roman Numeral Analysis. As a suggestion from Mark Gotham, I would like to integrate this parser within music21.

So far, the parser lives in this repository https://github.com/napulen/harmparser and consists in a single python file.

I would like to commit this parser on top of the latest music21 commit and make a PR.

I would like to ask in this issue two things:

mscuthbert commented 4 years ago

Would you be willing to relicense this as BSD? If so I think that we can make it work with music21 -- probably I'd just take the HarmDefs which are INCREDIBLE and so useful and also the def parse() call -- from there it wouldn't be too hard to put it within the music21/humdrum/spineParser file as a separate tool.

mscuthbert commented 4 years ago

It could also still be licensed under the Apache license -- I believe that the licenses are compatible.

napulen commented 4 years ago

The code has been relicensed as BSDv3 now.

napulen commented 4 years ago

Looking at music21/humdrum/spineParser now.

napulen commented 4 years ago

Comprehensive exams got in the way of this, sorry. Continuing this integration now.

When you say as a separate tool, how would you envision the use of this parser? I was thinking something like this:

  1. You parse a .krn file (e.g., music21.converter.parse('hello.krn'))
  2. Whenever you detect that the file has a **harm spine in it: 2.1 Run the **harm parser on every token that it finds within that spine 2.2 Use the output of the parser (e.g., root, inversion, is_secondary_chord, etc.) to store those properties in a music21 object. For example, at the same level of music21.pitch.Pitch, as a child of music21.note.Note or music21.note.Chord.

This workflow seems to require quite some work, maybe you thought of a simpler way?

Best.

MarkGotham commented 4 years ago

Greetings of the Season to you @napulen , @mscuthbert , and all the merry music21-ers around the world.

Thanks for your great work on this @napulen . I do indeed support this initiative which would fit nicely into music21's ever-improving suite of tools for harmonic analysis (Roman numeral and otherwise). The total coverage now extends to 'formats defined and demonstrated by Dmitri Tymoczko, Trevor De Clercq & David Temperley, and the DCMLab.' https://github.com/cuthbertlab/music21/commit/be057a002ecfa469308049766cc4bdf187763675 It would be great to see **harm added to that list and your code is a much more promising start than the hack I've been using!

Have a look at any of those converters to get a sense of the parts of music21 you need to connect with. Most importantly, and to answer your question, note that there is a roman.RomanNumeral class for storing all the task-specific information: https://web.mit.edu/music21/doc/moduleReference/moduleRoman.html?highlight=roman.romannumeral#music21.roman.RomanNumeral

Note also that music21's handling of the variable conventions for 6th/7th degrees in minor is rapidly improving. Check out music21.roman.Minor67Default: https://web.mit.edu/music21/doc/moduleReference/moduleRoman.html?highlight=roman.romannumeral#music21.roman.Minor67Default

You'll likely want to use that for input, though I don't think that conversion between different conventions is currently supported.

Thanks again for pitching in! As you'll doubtless know, one of music21's strongest assets is the way it can help bring all related work of this kind together. Here's to a bright, and better-coordinated future for the field.

napulen commented 4 years ago

Hi @MarkGotham and @mscuthbert. I had some extra time available this week and decided to go for this integration. Here is the pull request #561.

I had to debug from music21.converter.parse all the way to the stream generation. I leave my notes here for anyone interested in looking at / extending this work:

Extending music21's humdrum parser to support **harm spines

napulen commented 4 years ago

Lastly, for parsing **harm annotations, I used my old regex HarmDefs, which you are already familiar with. I added those as a module music21.humdrum.harmparser. I don't mind changing the license of that module according to what suites best for music21.

Given that said, for my own research, I do not use HarmDef anymore. I parse **harm annotations using a newer parser I've made, harmalysis, which is more robust and provides better validation of the syntax.

Specific examples where harmalysis is better than HarmDef:

I64             # handling numeric inversions additionally to the letter inversions used in **harm
IP7             # the quality of an interval is validated (sevenths can't be perfect, only major/minor/diminished/augmented). Interval quality is not validated in HarmDef
Vm9m7           # order of intervals (should be ascending according to **harm) is not validated in HarmDef
c:i             # changes of key within the annotation. This is necessary for parsing **harm-like annotations in MusicXML or MEI, where there is no straightforward mechanism to denote a change of key

... and other things mentioned in the MEC 2020 presentation.

I recommend using harmalysis instead of HarmDef eventually. The drawback is that you need one more dependency on your requirements.txt. I thought you may have reasons for not wanting a new dependency, so I went with HarmDef for this PR.