Skip to main content

finale-file-parser

A parser for Finale music notation files (.mus/.musx).

Finale was discontinued in 2024 and its format is proprietary and undocumented. This project reads those files and converts them to MusicXML, so scores that only exist as .mus or .musx can be migrated, analysed or archived.

Installing

uv tool install finale-file-parser     # just the `finale-parser` command
pip install finale-file-parser         # or as a library, into your own environment

Python 3.12 or newer. Runtime dependencies are defusedxml, which makes parsing untrusted XML safe, and Verovio, which engraves the inspection report's notation view.

Writing PDF needs one extra, because most people want MusicXML and should not pay for a PDF stack they never call. Ask for it by name — the quotes matter in zsh and bash:

uv tool install "finale-file-parser[pdf]"
pip install "finale-file-parser[pdf]"

It adds svglib and reportlab. Without it, --format pdf exits with a message naming the extra rather than a traceback.

To run a change that has landed on main but is not yet released:

uv tool install git+https://github.com/jsawruk/finale-file-parser

Using the library

from finale_file_parser import read_document, read_score

score = read_score("archive/score.musx")
document = read_document("archive/legacy-score.mus")

read_score is the ordinary high-level entry point and returns the format-neutral Score model. read_document stops one layer earlier at EnigmaDocument for callers that need Finale records. Both detect .mus versus .musx from the file contents rather than the filename extension, and preserve the existing parser exceptions.

Converting scores

finale-parser convert score.mus                 # writes score.musicxml beside it
finale-parser convert score.musx -o out.musicxml
finale-parser convert ./scores -o ./converted   # a whole tree, layout preserved

A batch does not stop at the first bad file: what fails is reported and skipped, the rest are converted, and the exit status is non-zero if anything was skipped. Existing output is never overwritten unless you pass --force.

Printing a score

pip install finale-file-parser[pdf]             # once; adds svglib and reportlab
finale-parser convert score.mus --format pdf    # writes score.pdf beside it
finale-parser convert ./scores -o ./printed --format pdf

--format pdf engraves the score and writes it as a multi-page PDF on US Letter, using the same notation the inspection report shows. PDF support is an optional extra so that installing the library does not pull in a PDF stack you may never use; without it, --format pdf exits with a message naming the extra rather than a traceback.

finale-parser inspect score.mus     # what the file is, and what was read from it

inspect prints the detected version and the shape of the score — parts, measures, events, pitches.

finale-parser inspect score.mus --report score-report.html

Writes one self-contained HTML file showing what the parser saw: how far the pipeline got, the engraved score and music tree it built, the records it read, and the raw bytes. A .musx report also shows the file's complete EnigmaXML as a foldable tree; a binary .mus has no source XML, so that tab is absent. The report is most informative when a document does not convert — it names the stage that stopped and why, which is what to send when reporting a file that will not parse.

A .mus is read by reverse engineering, so a converted score can be missing things the original had — part names come out positional, for instance. Later-era .mus title, composer and copyright metadata is preserved; the older DCL cohort carries no tagged bibliographic records. What is and is not carried is recorded in UNTRANSLATED in src/finale_file_parser/enigma/mus_document.py.

Looking at the raw pools

finale-parser extract score.mus     # writes score.mus.bin beside it

A .mus keeps its four pools compressed — the 2001–2005 era with PKWARE implode, which no hex editor can decompress — so extract writes them out in one file, framed with the container's own kind/length/checksum chain. Open that in ImHex with docs/formats/finale-mus.hexpat and the pattern walks the chain and lays out the records in the others and details pools — the 2011 era's variable-length records, the 2001–2005 era's 16-byte rows — at the offsets the parser uses. The entries and text pools stay raw bytes, because this project has no catalogued record shape for them and an invented one would be worse than none. A record's payload stays raw bytes, with the pattern listing which struct reads which tag for you to place over it by hand. The pattern is generated from formats/layouts.py, so it cannot state an offset the code does not.

How well does it read a file?

Of the 639 documents in the maintainer's test corpus, 632 build a score: 401 of 401 .musx, 99 of 99 Finale 2011 .mus, and 132 of 139 Finale 2001–2005 .mus. The seven are the files rather than the reader — six are blank scores the parser refuses deliberately, one an incomplete export.

That corpus is licensed material and is not in this repository, so those sweeps do not run in CI — they are skipped on any checkout without a corpus. CI verifies lint, formatting, mypy --strict, and every test that does not need one.

Requirements for development

  • uv (Python environment + package manager)
  • Python 3.12+ (uv can install this for you)

Getting started

make install     # create the virtual environment and install dependencies
make check       # run lint, format-check, type-check, and tests

Development

This project uses a single command surface — run make help to see every target. All code quality checks run together via make check, which is also what Continuous Integration (CI) runs on every push and pull request.

Contributor and AI-assistant conventions for this repo live in CLAUDE.md; design notes and decisions live in docs/.

make check is what CI runs, but a CI checkout has no corpus, so the corpus sweeps skip there. A green CI run verifies less than a green local run on a machine that has one.

License

MIT — see LICENSE.

The format knowledge this project rests on came from elsewhere, and two modules were written from third-party work: the PKWARE DCL decompressor is a port of Mark Adler's blast.c, and the score.dat cipher parameters were taken from denigma. THIRD-PARTY-NOTICES.md carries those notices; docs/REFERENCES.md records every source consulted.

The reference documents vendored in docs/ are third-party works reproduced for research and scholarship, and are not covered by this project's MIT license. They are documentation only — no published package contains them.

Metadata

Release files for finale-file-parser 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for finale-file-parser 0.4.0
File Size Uploaded
finale_file_parser-0.4.0.tar.gz 211.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for finale-file-parser 0.4.0
File Interpreter ABI Platform
finale_file_parser-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 441.9 kB

Release files / finale_file_parser-0.4.0.tar.gz

Download URL finale_file_parser-0.4.0.tar.gz
Size 211.6 kB
Tags Source
SHA-256 checksum
How to use checksums
2a796e4515ac133abbdef71a9bc026a92daa2f8b15c3849bde77ebca9fd5b606
BLAKE2b-256 checksum
How to use checksums
bb640d8d3d0ac39f71597730ecec4c6be596f8c784f34912d0795a07d707a6eb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.22 {"installer":{"name":"uv","version":"0.9.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / finale_file_parser-0.4.0-py3-none-any.whl

Download URL finale_file_parser-0.4.0-py3-none-any.whl
Size 230.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7887d8568c73a04c9cc99b3696b321d910b6a924cc0830da74051e629ead539f
BLAKE2b-256 checksum
How to use checksums
cb423ea648c5a29d399b516cbc5f98e64e1185a05011fc9ef46b03846c99803e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.22 {"installer":{"name":"uv","version":"0.9.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page