Skip to main content

Score Perfect to MusicXML converter

Project – | Article by Maarten Tromp | Published | 2103 words

The Score Perfect music notation software has been discontinued (its sole developer died in 2012), and there was no practical way to migrate scores to other software other than copying them by hand. I had recently been working on a few other reverse-engineering projects, so this looked interesting.

Details on the file format are available on the SPP 1–4 file format and SPP 5–7 file format pages.

From SPP to MusicXML with spp2musicxml
From SPP to MusicXML with spp2musicxml

In this article:

Background

My dad has been using this software since version 1, first on the Atari and later on Windows. All his scores are stuck in the SPP file format. And he is not the only one. I found a few MuseScore forum posts of people having the same problem.

Although SPP can export to MusicXML, it only exports the notes. Other information, such as chords and lyrics, is omitted. SPP can also export MIDI, but that loses even more information. My goal was therefore to write a converter that reads SPP files and outputs MusicXML. I worked on this project on and off over the past four years, for a few months in total.

SPP versions and timeline

Score Perfect was written by Klaus Kleinbrahm and first released for the Atari in 1990. It came in three editions: Education, Standard and Professional. Most people refer to the Professional edition as SPP, and this article does the same. Version 3 (1995) was also available for Windows, possibly running through Atari emulation. Version 5 (2007) was a Windows-only rewrite in Visual Basic 6.

In 2001, PriMus forked from SPP. Both SPP and PriMus can read files created before the split. SPP 5 (2007) introduced a new file format that PriMus could not read. SPP 5 Lite was also available, alongside the Standard and Professional editions.

I also came across a predecessor to SPP 1.0: Score Perfect 1.2-K (1988). It predates the release of SPP 1.0 by two years and uses yet another file format.

Disassembling the software

There were two ways I could see to investigate the file format: disassemble the Score Perfect software to see how it works, or dissect saved files. This section covers the first approach.

To analyse the software, or even to see what a score should look like, you first need a copy of it. My dad gave me his registered copies of SPP 5.2, 6.0 and 7.0, and I found several demo and Lite versions online. All the versions that are publicly available are listed in the downloads section of this article, along with information on how to run them and where they came from.

Next, I had to get those versions working under Wine. The Visual Basic versions required a whole bunch of DLLs and OCXs to be installed, with some of them registered as well.

Once the software was working, I started by looking at version 6.0. I had found VBDec, an open-source VB6 decompiler. Visual Basic can be compiled to P-code, the bytecode for the Visual Basic virtual machine, which is relatively easy to reverse-engineer. Unfortunately, SPP 6.0 turned out to be compiled to native code rather than P-code, so all I could get were disassembly listings. This is true of all the VB-based SPP versions. The binaries are also properly stripped, so almost all I could see were strings and DLL method calls.

I had heard that the Atari versions were written in GFA BASIC. Since GFA BASIC is also available for Windows, I wondered whether Score Perfect might retain its original GFA BASIC core, with Visual Basic serving only as a thin wrapper around the user interface. Some digging disproved this hypothesis: Score Perfect 5 is a complete rewrite.

I had gained some experience disassembling Windows binaries with Ghidra during reversing the EVBox ChargeStation Tool, so I put that experience to use in this project.

Searching the executable for the MusicXML note-duration names (quarter, eighth, and so on), I found the MusicXML export function. A bit more digging located the open-file dialog. Somewhere between those two functions should have been the code that accesses the score in memory. After several days of digging, I put this approach on hold and focused on the file format instead.

While working on the SPP 1–4 format, I went back to reverse-engineering.

The SPP 4.0 demo ran under Wine without any problems. It turned out to be a 16-bit Windows application. It looked a lot like native Atari applications, which strengthened my hypothesis that it was actually Atari software running in an emulator. The disassembly showed GFA-style procedure names (_P_demo_nicht), confirming that it was written in GFA BASIC.

Another way to investigate the SPP 1–4 format was through PriMus's son2emil converter. I used Claude Code while analysing the converter in Ghidra, to see how some of the remaining tags were interpreted.

Reverse engineering saved files

I ended up focusing mostly on the saved files, since I found them easier to understand than disassembled Visual Basic code.

The Score Perfect installations came with many demo songs created with various versions of SPP. Together with a number of my dad's songs, these formed a corpus for analysis.

Format versions

There are two versions of the *.SON format, not counting the predecessor: SPP 1–4 and SPP 5–7. The split between the 1–4 and 5–7 formats coincides with the rewrite of SPP in Visual Basic.

Starting on the SPP 5-7 format

After spending countless hours comparing serial communication dumps while turning a managed EV charger into stand-alone, I could now do the same for the file format.

I started with SPP 6 by creating an empty score and saving it. Then I added a single note, saved the file again and compared the differences. Repeating this process lets you build up a list of known bytes. Once you know what to look for, you can search the corpus for those bytes. After a few days of staring at hex dumps, I began to see the basic structure of the format. I then started writing a Python script to parse it.

Basic structure

The file starts with a magic number, followed by blocks containing settings, staff definitions, measures, notes, text objects, parts and, finally, MIDI mixer settings.

SON file
├── Magic
├── Header
├── Settings
├── Staff definitions
├── Measures
│   ├── Measure 1
│   ├── Measure 2
│   └── ...
├── Notes
│   ├── Measure 1
│   │   ├── Note 1
│   │   ├── Note 2
│   │   └── ...
│   ├── Measure 2
│   │   ├── Note 1
│   │   ├── Note 2
│   │   └── ...
│   └── ...
├── Text objects
├── Parts
└── MIDI settings

Some blocks have a fixed length or specify their length at the start. Others can be identified by their magic number. The remaining blocks have neither, so you have to parse their contents to determine where they end. The measures block, for instance, specifies its length in measures rather than bytes, and each measure has a variable length.

Most blocks were already present in SPP 5.0, but some were added by later versions.

Tags

An interesting feature of the measures and notes blocks is that they contain a fixed amount of data for each measure (measure number, number of notes and visual width) or note (note number, pitch, duration and horizontal position), followed by zero or more optional tags. Each tag has a single function and a predictable length. I have compiled a list of observed tags, including clef changes, key signature changes, time signature changes, volta brackets, bar lines and several tags whose functions are still unknown.

Some tags can be used with both notes and measures; others are specific to one type.

Most tags were already present in SPP 5.0, but later versions introduced some additional ones.

There are two basic note types. The shorter type can hold only a single quarter, eighth or sixteenth note. The longer type can hold any number of notes of any duration.

And this is just the tip of the iceberg.

I mapped out this structure by hand and built a list of blocks and possible tags. Whenever I thought I had found a new tag, I scanned the entire corpus to see whether the pattern held.

More analysis

I picked up this project again after I had created new firmware for a cheap relay board, which was my introduction to Claude Code. I used it again for this project, this time to analyse my documentation and the corpus of files. The analysis helped me identify and map missing tags, fields and blocks—work I had previously done by hand. I also used Claude Code to run statistical analyses of which tags appeared, and under what conditions.

A particularly useful demo song turned out to be Objektarten ("object types"). It shows every kind of object SPP supports, with its name alongside it. This became my reference for bar lines, dynamics, articulations, arpeggios, glissandos, ottavas and rehearsal marks.

The full versions of SPP would not accept the (valid) licence keys. I could still use them to some extent by ignoring the pop-up. Mouse input was blocked, but keyboard commands still worked.

I had previously created many test files by hand, each with slight variations, to find where specific data was stored in the file format. This time, I used AI-generated automation scripts for SPP running under Wine. It took some experimentation to get right, but I could then script the on-screen musical keyboard and all the buttons to generate many more test files. I could scan these for differences as well, which sped up the process.

Another way to investigate how things were stored would be to create MusicXML files containing the features I was interested in, import them into SPP, save them in the SPP format and analyse the results. Unfortunately, importing did not work at all in SPP 5, while SPP 6 and 7 imported only notes.

At some point, I knew most of the tags, but it was not always clear what each one did. To investigate, I took a screenshot of the original score in SPP, added or removed a single tag, took another screenshot and compared the two. I could also change tag parameters to see what effect specific values had. For example, I created six versions of the same song, each with a different clef, to work out which value corresponded to which clef.

The SPP 1-4 format

To investigate the SPP 1–4 format, I installed several SPP 1–4 demos, which came with many more songs. I resaved all the new songs in SPP 5.1 using the SPP automation. This considerably increased the size of the corpus and made it possible to identify more of the 5–7 format.

I also exported the same files via PriMus's son2emil converter. Comparing the three versions of the same file helped me identify the older format.

The 1–4 format is quite similar to the 5–7 format. As it originated on the Atari, values are big-endian and strings mostly use the Atari character encoding. However, the structures are built in more or less the same way as those in the 5–7 format. Tones use the same numbering, which made it easy to locate that part in the files.

Working out the details involved changing individual values and reloading the files to see what had changed, but that was mostly mechanical.

The spp2musicxml converter

The result of all this work, apart from the documentation, is the spp2musicxml converter. It is a complete rewrite of the code I wrote during the reverse-engineering process. The converter is open source, so anyone can see how it works and help improve it.

The requirements are modest. All you need is a reasonably recent version of Python (3.11 or later). The application is packaged as a PYZ file, so you can simply run it. It has been tested on Windows and Linux, and should also work on macOS. The converter supports both CLI and GUI modes. The GUI uses tkinter, which is typically included with Python.

The converter supports files created with SPP 1–7. The older Score Perfect 1.2-K format is not supported.

Not all layout information can be transferred, because MusicXML does not support all of it. Unknown values are ignored and omitted. This includes some variations of hairpins.

The converter's output is validated against the MusicXML 4.0 schema, and all the songs in the corpus open in MuseScore 4.

Open source

Everything in this project was done using free and open-source software wherever possible.

Accordingly, the software, article and documentation created for this project are released into the public domain. You can find the relevant files in the downloads directory for this article.

This does not apply to anything I did not create myself, such as the Score Perfect and PriMus software and their demo songs. Those are not mine to give away.

Closing thoughts

Not every part of the file formats is fully understood, but most of the remaining unknowns concern layout information, which MusicXML cannot represent anyway.

The Score Perfect 1.2-K format is only outlined on the format page and is not supported by the converter. I do not see a realistic use case for digging deeper into it.

SPP format support could also be added to MuseScore's import functionality.

Good luck migrating all your Score Perfect files to MusicXML!