Tuesday, September 1, 2020

Music21 v6 Released!

Music21 v6 is OUT as v6.1.0! This represents over 500 commits over the past 14 months since v5.7 was released.

Many thanks to Jacob Tyler Walls (JTW below) who made so many contributions to the v6 branch, both big and small. Mad props and round of applause!

v6 fully supports Python 3.8 and removes support for Python 3.5. v6 will be the last major release to support Python 3.6 and we will work on Python 3.9 support immediately.

As with all new v.X releases of music21 there are improvements and some backwards incompatible changes (not too many)

In no particular order, here are the things to look for in the new music21 v6:

  • Type hints throughout music21 -- when you program with a sophisticated IDE you will be able to see what is returned and required as attributes in much of music21.

  • music21 is no longer packaged with external modules; these will be installed when installing music21 via pip (otherwise run pip install -r requirements.txt). Speed and security improvements come with this.

  • converter.parse('file.mid', quantizePost=False) will let you load in a MIDI file without any quantizing. (Thanks JTW)

  • Lots more values cached = faster music21; RomanNumerals in particular are over an order of magnitude faster, and Chords are faster too. If you ever have a problem, all Music21Objects have a .clearCache() function. Just add @cacheMethod as a decorator to a method and it will use the caching routine.

  • Intervals have been rewritten to use properties entirely. So whatever happens to an Interval, its semiSimpleNiceName (to take one of dozens of examples) will always be up to date. Intervals use Specifiers that are enums like interval. Specifier.PERFECTinstead of inscrutable numbers. (those inscrutable numbers still work though) Specifiers are more than just standard enums -- they can invert themselves, do semitonesAboveMajor() etc. Interval geeks rejoice.

  • Intervals given a noteStart and a name will generate noteEnd automagically. They also get transposePitch() improvements along the way.

  • KeySignature gets transposePitchFromC() which takes a pitch in C major and returns the pitch in the same scale degree in this key..

  • Voices with gaps in them work way better in MusicXML. Repeat endings with multiple numbers like 1,2 r/t with musicxml (JTW)

  • TSV files which encode harmonic analysis can now be parsed (Thanks Mark Gotham)

  • Humdrum harm spines parse now, giving another way of encoding harmonic analysis. (Thanks Néstor Nápoles López)

  • MIDI refactor: easier to subclass and extend and with many docs -- keeps a clear distinction between strings and bytes. MIDI values are all Enum classes (but IntEnums so they compare well with pure numbers)

  • StripTies works much better thanks to JTW -- and does not filter out non-Notes anymore (technically an incompatibility, but really a bug fix). Voices, etc. don't faze .stripTies() any more. Stream.voicesToParts() also preserves more elements. Chords with some notes tied and some don't no longer get merged. Note that stripTies retainContainers defaults to True now, because getting the opposite behavior is as easy as a .flat

  • Internal but important: lots of parts of music21 code that used abbreviations now spell out the whole word. This is important for our friends using screen readers. While music21 will (for historical/compatibility reasons) still use camelCase for all method names, attribute names, etc., internal variables may now use underscore_case which helps with screen readers.

  • Stream().write('mxl') will write compressed musicxml (JTW). Or write('...', format='musicxml', compress=True)

  • Improvements to VoiceLeadingQuartets (thanks Ryaan Ahmed), including finding parallel intervals with octave displacement. voiceCrossing and voiceOverlap detection.

  • Substantial improvements to RomanText: see https://dmitri.mycpanel.princeton.edu/romantext.pdf

  • RomanNumerals can specify how they represent ^6 and ^7 in minor with sixthMinor and seventhMinor keywords)

  • OMR fixers can recognize Turns and other ornaments. More merging here to come. Thanks Janelle Sands!

  • Substantial improvements to beaming routines and tests (thanks Almog Cohen!)

  • Keys now have .deriveByDegree() like Scales, so "What minor key has scale degree 3 as B-flat?" can easily be answered.

  • Improvements to dotted tuplets (Almog Cohen)

  • Chord.name/fullName now gives better names for common chords like Major/Minor triads -- no longer relies entirely on chord.tables. But even there many improvements and spelling corrections.

  • Chords expose .notes to get at the notes that make up a chord, in a different way than Chord[0] or for n in Chord -- I'm still an old Perl guy, sometimes There's More Than One Way to Do It, and this way makes some things conceptually much easier and faster.

  • More Bach Chorale fixes from Doctor Schmidt (thanks!)

  • Stream.replace(recurse=True) finally works!

  • StringIndication, FretIndication work in musicxml (thanks Peter Mitrano)

  • For those who are adventurous -- intervalNetworks are now exposed on scales.

  • prebase.ProtoM21Object -- an idea ported back from music21j: nearly every object in music21, including all Music21Objects, inherits from this super-lightweight base class which allows for querying classes and giving consistent representations. It makes working in music21 a lot more fun.

Incompatible Changes

  • Stream filters now return a new StreamIterator. So old code like:
s = stream.Stream()
sIter = s.iter
sIter.getElementsByClass('Chord')
list(sIter)

should now be written as:

s = stream.Stream()
sIter = s.iter
sIter2 = sIter.getElementsByClass('Chord')
list(sIter2)

For most people using filters within a for-loop, nothing will appear to have changed.

  • if element in stream now only returns True if the element is actually in the Stream, not if element is equal to something in the Stream. This is not normal Python behavior for __contains__ but it is something music21 users have counted on for a decade, and now it is assured.

  • MIDI has been completely refactored, so if you are mucking with MIDI internals, it's going to be completely different. If you're just using converter.parse and .write('midi') it won't have changed much.

Also

  • Setting a pitch's accidental to a number is deprecated and to be removed soon. use b = pitch.Pitch('B4'); b.accidental = pitch.Accidental(-1).
  • some long deprecated functions removed, such as Stream.restoreActiveSites, Stream. _yieldReverseUpwardsSearch, common.standardDeviation (use statistics.stdev instead).
  • Chord sort methods no longer return the chord itself if inPlace=True
  • interval.convertSemitoneToSpecifierGenericMicrotone, convertSemitoneToSpecifierGeneric now return a Specifier Enum as their first value
  • Spelling corrections that are technically incompatible: Interval.perfectable replaces typo Interval.prefectable. Appoggiatura is spelled correctly with two-gs everywhere it's used (JTW)
  • stripTies(retainContainers=True) is now the default.
  • Half-diminished chords have ø7 etc. as their default representation.
  • MIDI.intsToHexString has become MIDI.intsToHexBytes which does what is says it does.

Small Changes/Bugs squashed:

  • A malformed spanner in musicxml will no longer crash parsing.
  • Tuplets are equal if their durations are equal. Better docs for duration equality.
  • contextSitesnextprevious, and getContextByClass have a priorityTargetOnly or activeSiteOnly keyword for searching activeSite only. Speed!
  • From music21.X import * works much more reliably.
  • Bugs fixed in feature extraction of keys.
  • serial allows "P" or "T" to be used for transpositions
  • Tone-Rows give their row in the repr.
  • Historical tone-rows no longer have Row in their name. For instance: serial.getHistoricalRowByName('SchoenbergOp37').matrix() -- the old form still works though.
  • Feature extraction on empty streams (or ones that don't have instruments or something else) works properly (JTW)
  • Feature output formats can set .ext directly, in case you need a different extension.
  • viio7 can be specified as vii07
  • Interval: reverse=True works properly
  • subprocess.run is used instead of os.system for PNG generation. Thanks Uğur Güney. Fixes using musescore with music21 in Jupyter when spaces appear in filenames. (also thanks to Frank Zalkow)
  • Better representation for many objects
  • Error handling for incorrect chord abbreviation is improved. Thanks Vikram Natarajan
  • Librettists and lyricists can be searched in metadata.  .age() works properly for living composers.
  • MIDI plays back properly in Jupyter again BUT also does not add a delay when writing out a file.
  • Voice numbers in generated musicxml are now low numbers
  • pitch.Accidental gets . setAttributeIndependently() in case you want something to look like a flat but only alter 0.8 semitones. (This appeared in v.5 at some point but was never announced)
  • Note gets a pitchChanged() method which is called by its attached pitch anytime it changes so that caches can be used. Pitch gets an informClient() method which is called anytime the pitch itself changes. There is something similar in Chords too. This squashes a lot of bugs where pitches were changing but notes/chords/scales/etc. did not act like they had changed.
  • makeAccidentals works properly in Voices (JTW)
  • MIDI parsing gets more instrument objects from more places (JTW)
  • zero-length objects at the end of streams were being ignored by makeNotation (JTW)
  • Show formats: MuseScore 3.5 compatibility. (JTW). Preview is found by default on macOS Catalina/Big Sur -- still works for older OSes. No more 'is your doc > 999 pages?' bugs!
  • Some Neo-Riemannian docs weren't showing (thanks Adam Spiers)
  • Tone profiles for key analysis have been corrected. (thanks Micah Walter)
  • scaleDegreeWithAlteration on Augmented 6th chords works.
  • .musicxml is fully recognized as a suffix.
  • Improvements to analysis.windowed (thanks Sandro Luck)
  • RepeatExpander now does nothing on a score with no repeats. Before, it crashed.
  • Sousa example couldn't show before (thanks David H. Gutteridge)
  • Verticality.removeVerticalDissonances now works (also thanks to Gutteridge)
  • Z-relation for 5Z37 (5Z17) was incorrect (thanks Rodrigo Balthar Furman for spotting this)
  • Power-users who subclass Stream to be standard storage variants: Stream().coreSelfActiveSite(el) allows for subclassing what happens when an element should normally have its activeSite set to the stream.
  • Lilypond output with UTF-8 works. Grace notes no longer crash Lilypond. Now that there is a 64-bit binary Mac version of Lilypond, it will not be removed from music21.
  • RomanNumerals compare with each other. __eq__ logically defined.
  • Modest performance improvements in sorting (Thanks Alexander Morgan)
  • Documentation and test improvements and a few bugs squashed in ABC parsing.
  • f-strings used throughout music21 allowing for more detailed error messages and many bugs to be detected and removed.

Deprecations

  • interval.Interval.convertSpecifier() deprecated. use parseSpecifier instead.
  • Humdrum parseData() and parseFile() are deprecated. use the general converter.parse() instead.
  • .editorial.misc is deprecated, just stick whatever you want on the .editorial object itself.

Gratitude

As always, I want to thank MIT, the NEH, and the Seaver Institute for supporting music21 over the years along with the community of developers who use and give back to music21.

Saturday, December 28, 2019

music21 5.7.2 released -- Python 3.8 supported

Music21 v5.7.2 has been released.

This release adds basic support for running music21 v5 on Python 3.8 — the full test suite does not pass on py 3.8 but the differences there appear to be cosmetic.

If you are using Python 3.8 run:

pip install --upgrade music21

Saturday, June 8, 2019

music21 v5.7 released

Music21 v5.7 is the fourth and final release in the v5 series. It brings under the hood fixes -- lots of bug fixes and speed ups and some new features -- from 126 commits since last October.
Download by running from command line:
pip install --upgrade music21
This is the last release of the v5 series so that work on v6 which may bring some backwards incompatible changes -- all deprecated features will be removed, as will support for Python 3.5 (by the time v6 is released, Py3.8 will be out and this keeps w/ music21 policy of supporting the last three Python releases). The release of v5.7 also marks the End-of-Life for music21 v4 (the last release to support Python 2.7); if someone wants to continue supporting v4/Python 2.7 in music21, please let me know, but I'm so happy with how quickly almost all of us have moved to Python 3. Looking forward to major tablature improvements, SMuFL support, and much faster development thanks to "f-strings" in v6.
The music21 user community continues to be active and robust as attested by the number of contributed features below. Thanks for keeping me sustained during my non-music Admin time! Among the most notable improvements are (in reverse chronological order):
  • Type hints on major objects and functions in the library and the promise of more (once Py3.5 is dropped) soon. Those who use fancy IDEs like PyCharm will reap major bug checks!
  • The most extensive typo-checking / spell-checking / linting ever done to music21 -- the code now has that new car smell.
  • Stream.measures(4, "5a") -- measure suffixes work in many more places in the code.
  • Pizzicato notes export properly in MusicXML
  • Chorale metadata improved (thanks to @doctor-schmidt!)
  • Improvements to MEI accidental handling (kudos @raffazizzi)
  • KeySignatures work better in multi-voice contexts (thanks @pconerly)
  • Bugs that made .getContextByClass() sometimes inconsistent (especially with forward searches) fixed.
  • Harmony objects (ChordSymbols, etc.) sort before Notes so that the harmony of a note at the offset can be found easier. And proper musicxml offset support (thanks @a-papadopoulos)
  • Chord.notes gives direct access to the underlying Note objects that make up a Chord. Chords also get a good __eq__ test.
  • MuseScore 3 support (thanks @YoWenqin)
  • Accidental fixes for ABC (thanks @dvdrndlph)
  • Added Neo-Riemannian analysis features (thanks @MarkGotham) and triads can now know which hexatonic system they belong to.
  • Fixes to MusicXML chord input.
  • Barlines now use "type=double" instead of "style=double", since in v6 they will get full style.Stylebehavior.
  • User's Guide gets the long awaited Chapter 58 on Sites and Contexts for advanced users.
  • Clefs get a .name property so that the class does not need to be parsed.
  • Grace notes no longer cause obscure bugs in Stream.makeNotation().
  • A bug in Interval.direction for some perfect intervals is fixed (thanks @ryaanahmed and www.artusi.xyzteam for identifying the bug)
As always, I want to acknowledge MIT Music and Theater Arts and the School of Humanities, Arts, and Social Sciences for encouraging the development of music21. Founding contributions to music21 were made by the Seaver Institute and the National Endowment for the Humanities.

Monday, October 29, 2018

music21 v5.5 released

Version 5.5 brings some small but important improvements to music21 and is a maintenance release. V5.5 is the first release to no longer support Python 3.4, so please use Python 3.5 or higher to upgrade.
Important changes and bugfixes.
  1. Chord.commonName gets many improvements, detecting for instance, the differences between dominant-seventh chords and augmented sixths.
  2. much better metadata on Bach Chorals (thanks Norman!)
  3. Stream.voiceToParts() is much improved and gets an optional separateById keyword argument which will only merge voices whose .id matches. -- this is very exciting to me from an analytical perspective and one reason I decided to release now.
  4. Volpiano is upgraded to a full conversion method. Most people will go, "buh??" people who work with Gregorian chant will rejoice.
  5. obj.activeSite is set more reliably.
  6. better error messages if a stream cannot be chordified.
  7. ABC parses all major and minor keys properly -- there were a few bugs especially on sharp keys in minor before. (Thanks @a-papadopoulos )
  8. "Cad64" is now an acceptable RomanNumeral alternative for "I64" for all you theorists out there who cringed when writing it. Resolves to I64 in major context (or as a secondary dominant) and i64 in minor contexts.
  9. bug fixes on rounding approximate durations (such as in MIDI parsing)
  10. converter.parse('tinyNotation: ...', raiseExceptions=True) allows tinyNotation to bubble up anything that prevents reading a token. off by default, but useful for debugging. Expect this keyword to start appearing (w/ default False) for other conversion methods.
  11. Changed: as promised years ago, the default on Stream.recurse() is now NOT to include the caller itself in recursion. Use keyword argument "includeSelf=True" to do so. Recurse keywords are keyword only.
  12. Changed: arguments to Music21Object.contextSites are keyword only. This fixes a few little bugs. Same with Stream.elementsChanged()
  13. MusicXML stores Fingerings properly (thanks @hofst !)
  14. Fixes for extreme pitchbends (very close to wheel minimum) on MIDI.
  15. Dropped bug-fixes for very old versions of Sibelius and MuseScore < 2.0
  16. Added bug catching for MusicXML that gives the shape of the clef ('G', 'F') but no line -- uses sensible defaults. And many other cases in MusicXML where an attribute is empty (as one musicxml writer is now producing)
As always thanks to MIT, the Seaver Institute, and the NEH for funding early development of music21.

Upgrade with “pip install --upgrade music21”