Skip to main content

Score Perfect Professional 1-4 file format

Article by Maarten Tromp | Published | 5389 words

This document describes the *.son file format of Score Perfect Professional up to version 4, as far as it is known.

Background information on the project can be found on the project page. This document describes the .son files of Score Perfect Professional (SPP) 1.4 to 4.94, as far as they are understood: enough to convert their notes, lyrics, marks, slurs, hairpins, texts, voltas, clef changes, multi-measure rests, ottavas and grace notes to MusicXML. Each finding says how it was checked. What is known, guessed and still unknown is listed under Status.

Many fields are those of the SPP 5–7 format, in another arrangement: both are by the same developer. Where a code is the same, this page links to the SPP 5-7 file format page, which has the tables.

The converter, spp2musicxml, implements what is described here (spp4.py); where this document and the converter differ, the converter is more likely right.

In this article:

Status

How far this format is understood, in numbers, and what is known, guessed and still open. The numbers are against the 116 songs of the corpus that SPP Lite 5.1 could open and save again in the SPP 5–7 format (the twins), and against PriMus' son2emil. Of the 119 songs of this format in the corpus, the 3 of SPP 1.0 cannot be opened by Lite; all 119 are read, and the 5 of SPP 1.2-K are another format.

Statistics

whatresult
songs read119 of 119; 116 with a twin
measuresthe number agrees with the twin in 116 of 116
notes205,203 notes in the twins, 204,942 of them the same in type, dots, rest and pitch (99.9 %)
time signatures, voltasthe same as the twin in 116 of 116 songs, measure by measure
bar lines112 of 116 songs (the rest differ by multi-measure rests)
grace notes369, the same as the twins in 116 of 116 songs
marks and lyrics96 % of the notes that have them agree with the twins; most of the rest are one note off, in songs with several voices on a staff
slurs7484 starts and stops the same as SPP Lite 5.1's, 344 only ours and 140 only SPP Lite's (where it differs it is shifted, as with voltas and texts)
hairpins271 ends the same as son2emil's, 7 only ours and 5 only son2emil's
tempoequal to the twin in 116 of 116
titlethe same as the twin's in 67 of the 69 songs that kept one (the other two differ in the characters)

What the reader finds in these songs and does not understand (the finding, its count, and in how many songs):

findingcountsongswhat it is
tie to another pitch73a tie to a note that does not have the tone: 2 songs' worth of single cases (it was 2529 when the flag below was taken for all tones)
slur ends outside the song8215the slurs where SPP Lite's differs, above
unknown ornament 7e1441a small zigzag below the note: tried in SPP Lite 5.1, see the marks of the SPP 5–7 format
unknown ornaments 84, 88, 90, 6b, 13, 1224, 17, 12, 12, 9, 51 to 3 eacha bass clef sign, two short strokes, a small wave, a natural and a flat sign: tried in SPP Lite 5.1 (same marks page); the converter writes the natural and the flat
optional records not understood, mask 10 and 0460 and 282 and 1a record after the tones with a mask bit that does not read as it should

Known

Checked against the twins, son2emil or SPP's own screen (each finding below says how):

  • The header, the staff records (vertical place, transposition, clef, number of measures, instrument), the time signature table and the three per-measure arrays (time signature, key, bar line).
  • The notes: type, dots, stem, beams, cue, tones and accidentals, ties, rests; the tempo, the title rule; texts, voltas, clef changes, multi-measure rests, ottavas (codes 1 to 8), grace notes, tuplets.
  • Optional records: lyrics, marks (the codes known from the SPP 5–7 format), slurs (also where they end), hairpins (also where they end, from the layout as son2emil does).
  • The layout strings and the zoom of each line, which hairpins need.

Guessed

Read and used, but not checked as far as they could be:

  • Staff record byte 2 (the number of staff lines, 5, and 1 in the song of chord diagrams), byte 14 (the voice) and the widths in bytes 2 and 3 of a note (they grow with the lyric syllable).
  • The 5 bytes after the ottava code of f3 (the third perhaps where the line ends), and ottava codes other than the one seen.
  • f2: a jump up or down, perhaps a note drawn on the other staff; f1: two offsets.
  • The 40 byte of the fd mask (a byte 5b to 72, read by son2emil and not used), and e8 (a dotted slur, hidden stems: from son2emil's names).
  • Bit 80 of the bar line byte (hide staff) and the group barline style (bits 10 to 40 of the second measure): son2emil's names, not seen in SPP.

Unknown

  • The e8 record (a drawing matter only: the notes it is on read the same in SPP Lite 5.1's resaves as those without) after f6 (5 notes in the corpus), the f6 record f5, and the values 01 and 04 of f5, f1, f0, ed, eb, ea, e9; bit 02 of the fd mask. Son2emil reads most and does not use them.
  • The optional records with the masks 10 and 04 that do not read.
  • The first list after the measure arrays, with records of 6 bytes (Voltas); the byte before the key and the 2 after it in the header.
  • Everything that only decides the layout of the page, which this reader does not need, and which was left alone: most of the settings record of 122 bytes (the numbers for the spacing and the zoom are known), the numbered sections between the texts and the notes (03, 04, 05, 08, 09, 0a, 0b, 0d, 0e), the stretches between the blocks, and the settings of the SPP 1.4 song (its layout is not found).

How the rest could be found

SPP 4's demos cannot save, but SPP Lite 5.1 opens the songs of this format and draws them. So a byte of an old song can be changed, the copy opened in Lite and looked at, and Lite's own resave compared with the twin of the original, as was done for the SPP 5–7 format. PriMus' son2emil is the other oracle: it names what it finds (the words of its text format), also for bytes that SPP Lite does not draw.

Versions

Files up to SPP 4 start with f0 ee ed. The corpus holds 119 of them: 18 of the songs the converter was written for, the songs that come with the SPP 4.0 demo, those of the SPS and SPE 4.0 demos, and 3 of the SPP 1.0 disk. Their version strings are 11.0, 11.4, 12.0, 12.1, 13.0, 13.1, 14.21, 14.90, 14.92, 14.93 and 14.94: a 1, then the version of SPP that saved them (son2emil names 13.0 format version 3, and 14.94 4.94), so from SPP 1.4 to 4.94. The layout found so far is the same in all of them.

SPP 1 and 2 ran on the Atari platform only. SPP 3 and 4 were also available for Windows, and later for the Mac, presumably by emulating the Atari: the Windows version of SPP 4 looks like an Atari program, drawing its own menu bar. The format fits that origin: big endian, the byte order of the Atari's 68000 processor, and text in the Atari's character set. SPP 5 was a rewrite, for Windows only, with the SPP 5–7 format. SPP 4 reads all these files; the demo of SPP 4.0 opens them but cannot save. SPP Lite 5.1 opens them too, down to SPP 1.4, and saves them in the SPP 5–7 format.

The Standard (SPS) and Education (SPE) editions are assumed to use the same format as SPP: Scoretec offered version 4.0 in all three, and later said that songs could be exchanged between them. Their 4.0 demos cannot save, but come with songs (8 for SPS, 26 for SPE): all start with f0 ee ed, carry the same version strings (11.4 to 14.93), read with the same reader and convert without errors. 15 of them are byte for byte the demo songs of SPP 1.4 to 4.21; the other 19 (from SPP 4.90 to 4.93) are not in the SPP demos.

SPP 1.0 (version string 11.0) writes this format already. SPP 1.2-K, the short version of 1.2 ("Kurzanleitung"; the program says 1988 and 15.7.1989, a year before SPP 1.0 of 26.6.1990) that came on the utility disk of the magazine KEYS and was made for it, by the same author, Klaus Kleinbrahm, a music teacher (his own words in the disk's LIESMICH.TXT; it needs an Atari ST with 1 MB and runs in the high resolution only, as all of them do: "läuft nur in der hohen Auflösung"). The text says that songs made with 1.2-K can be loaded and edited by the main program, Score Perfect itself. Unlike SPP 1.0, it does not write this format: its songs start with f0 ee ec and are text, lines ending in 0d 0a as written by a BASIC program: the version "1.2", the title, a line of numbers, then per measure a line with the length of its notes and a line with the notes in quotes, as fe, number, type, position records. Not decoded; the converter says "SPP 1.2 format: not supported". The corpus has 5 of them, from the SPP 1.2 disk image.

It is the format of the time before PriMus forked off from SPP: PriMus' son2emil.exe (in PriMus Free) converts these files to its text format, emil, and refuses the SPP 5–7 format.

One file of the SPP 4.0 demo (Lieder - ALTA_TRI.SON) is no song: all its 7708 bytes are e5, so the data was never written properly or was lost in copying.

How it was decoded

Every song was saved again by SPP Lite 5.1, in the SPP 5–7 format, driven by a script under Wine; the converter reads those, and all 116 convert to valid MusicXML, with as many measures as son2emil counts where it has been run. The 3 songs of SPP 1.0 have no pair: SPP Lite 5.1 cannot open them, and saves an empty song; the SPE and SPS songs have no son2emil output. So each song comes as a pair: the same music in both formats. research/oldformat.py lines the SPP 1–4 file up against the converter's reading of the SPP 5–7 one, field by field; research/compare_older.py compares the converter's reading of both. son2emil's output of the originals is a second reference. In the corpus, the resaved songs are in songs/old_resaved_by_spp51/ and son2emil's output in songs/old_converted_by_son2emil/, each in the same collection and version folders as the originals.

What SPP 5.1 makes of these songs, and what that taught about the SPP 5–7 format, is on the SPP 5-7 file format page.

Later findings came from two other sources. PriMus' son2emil.exe was decompiled with Ghidra: it reads the notes' optional records, the lists after the measure arrays, the layout and the hairpins, so its code says what a field is (the f6 record, the hairpin walk). The test songs research/test_songs/maarten/ were made by hand in SPP for the SPP 5–7 format, with one thing changed at a time, and are on the SPP 5–7 format's page.

Conventions

File layout

From the header to the time signature table, the file is read block by block (in SPP 2 and later); the notes are found by their shape, as the sections before them are not all decoded. research/layout_older.py prints this map for a song.

blockcontentshow it is found
headermagic, version, title, counts, then 18 bytes: the first time signature, a byte, the key and 2 bytesfixed layout
staff recordsper staff its name and 15 bytesright after the header
settings and staff groupssettings of a fixed length, then three lists of staff ranges and 2 bytesright after the staff records
time signatures, keys and bar linesa table of time signatures, then three arrays of a byte per measureright after the staff groups; by its shape in SPP 1.4
listsa list of records of 6 bytes (mostly empty), then the voltas, each a length and recordsright after the arrays
texts and sectionsrecords separated by ff ff: the texts, then numbered sections: the clef changes (02), the MIDI mixer (06) and others, not decodedright after the voltas
layout stringsper measure a string with its width and the places of its columns, and one morethe last thing before the notes: chained back from where the notes start
notesper staff, one block per measureby its shape, with the stored counts of the staff records
linespages and lines: per line the zoom, its first and last measureafter the notes

All 119 songs are read this way.

Staff records

Right after the header, per staff: its name, a string, then 15 bytes.

bytesmeaning
0-1the vertical place, probably: about 60 apart from staff to staff
2the number of staff lines, probably: 05, and 01 in the song of chord diagrams
3the transposition
4?
5-6the length of the first measure in divisions: less than the time signature's in an upbeat (the SPP 5–7 format's measure tag c9)
7the clef (00–04 are read), with the SPP 5–7 format's codes: 00 treble, 01 bass, 03 alto
8-9the number of measures stored for the staff, up to its last measure with notes: empty measures after it are left out
10?
11the instrument: shared by its staves (a piano: Bach's fugue has four records, all instrument 01)
12-13?
14the voice on the staff: 01, 02, or 00 when the staff has one. Two records with the same vertical place are two voices drawn on one staff: the fugue's records come in pairs (00 70, 00 b3), as do Beethoven 5's two flutes

Transposition

Signed, in fifths: how much higher the written key is than the concert key. 02 clarinets in B♭, 03 horns in E♭, fe (−2) a part in D (Beethoven 5, Mozart). The sounding pitch is the written pitch minus 7 times that in semitones, within an octave: −2, +3 (or −9), +2. Checked against the MIDI pitches of the resaved songs.

Settings and staff groups

Right after the staff records: settings of a fixed length, 122 bytes from SPP 3.0 on, 121 in SPP 2 (the one song of SPP 1.4 has shorter ones, not worked out). The 6th short (bytes 10-11) is the tempo in beats per minute, equal to the tempo of the resaved songs in all 116; Bach's Toccata has 88. They start with 00 05 to 00 08 and hold the guitar tuning 34 39 3e 43 47 4c (E A D G B E, as in the SPP 5–7 format); not decoded further.

Then three lists of staff ranges: each its length in bytes, then pairs of staff numbers, first and last. The first list holds the braces, the second the brackets, as the SPP 5–7 format's part groups (in the other order); they agree with those of the resaved songs in all songs. The third list, other ranges, comes back unchanged as list 6 of the SPP 5–7 format. Beethoven 5: braces 19-20, brackets 1-8, 9-13 and 14-20. Then 2 bytes (00 01, 00 02 or 00 04), and then the time signature table.

Time signatures, keys and bar lines

A table of the time signatures the song uses: its length in bytes, then 12-byte entries:

bytesmeaning
0the style, as in the SPP 5–7 format's time signature: 01 C, 02 cut C, 04 numbers; 00 in two songs, shown as numbers
1?
2-3the beats and the beat type; 04 04 for C and cut C, which the style overrides, as in the SPP 5–7 format
4-904 04 04 04 00 00
10-11the measure length in divisions: 0c 00 is 3072, 4/4 (the SPP 5–7 format's measure tag c8)

After it three arrays, each the number of measures, then a byte per measure:

Against the resaved songs, the keys agree in 96 songs, the time signatures in 94 and the bar lines in 88; the rest differ by multi-measure rests and the bits below.

The bar line byte has more than the bar line, found by reading son2emil: bit 80 is "hide this staff" (son2emil writes HideStaff after the bar line, and ShowStaff where the next measure has the bit off), and bits 10, 20 and 40 of the second measure's byte are one song-wide number, the style of the barline through a staff group that son2emil writes in Set grouping: 0 continuous (1-2), 1 1:2, 2 m, 3 and others other characters (+ is 5, ' is 6). The other measures repeat the bits (Beethoven's Fifth: all 502 measures 3x, Locus iste: all 48 1x), which is why they are on every measure of a song or on none. The converter reads none of these.

Voltas

Right after the per-measure arrays come lists, each with its length in bytes first. The first list has records of 6 bytes, ff, 00, a measure, a kind and 2 bytes, for example ff 00 0f 05 31 54; it is empty in most songs (00 00) and its meaning is unknown (kinds 1, 2, 4, 5, 7, 9, 11, 13 occur, and two measures of Beethoven's Fifth, second movement, where SPP Lite 5.1 numbers one lower have a kind 1 record). The second list holds the voltas: 00 7e bytes (7 per record), then per record ff ff and 5 bytes: 00, the measure (from 1), the number of the ending, 78 00. A volta of several measures has a record for each, so the first measure of a run of the same number is where SPP 5.1 puts it. After the voltas come the texts. All 115 voltas of the 97 songs are where SPP Lite 5.1 has them; before the first list was taken into account, five big songs had none and the records of the MEL songs seemed to have a first byte that was not 00.

The bar line codes 08 to 0f of the low bits are not known: the converter logs them. The bar line bits (see above) have nothing to do with voltas: none of the measures with a volta has any of them.

Texts

After the voltas: records separated by ff ff, first the texts, then the numbered sections (below). A text:

lengthmeaning
1the staff, from 1
2the measure, from 1
2 + 2the place: x and y
1the style: 01 the title
3?
1the length
the lengththe text

The converter puts every text above the staff; the title is the first of the texts in the title style (the lowest measure, then the lowest staff, then the first in the file: the same as SPP Lite 5.1 makes of it in 67 of the 69 songs that kept a title) and is not kept as a text, the others are texts, and a header title that is "Kein Titel" is none; there is no composer or subtitle field in this format, so Toccata's "J. S. Bach", in the same style, stays a text and not the title as the header's own title string has it, and a text outside the song, or one whose length does not fit, is skipped. The header's title is often "Kein Titel"; the text in the title's style is the real one ("Menuett II", "INVENTIO 1"). 565 of the 870 texts of the resaved songs match; SPP 5.1 itself drops texts and moves some in long songs (by 156 measures in Beethoven's Rondo), where son2emil agrees with the original.

After the texts the file goes on with records separated by ff ff, each starting with its number: 02 the clef changes, if there are any (see Clef changes); 03, 04 and 05, short and not decoded; 06 the MIDI mixer, with its labels in Atari text (LAUTSTÄRKE, HALL, CHORUS, PAN), not decoded; then in most songs 08, 09, 0a, 0b and 0d or 0e, not decoded; some songs have a section 00 before the texts. A section can hold ff ff itself, so they cannot be cut apart by splitting. In some songs section 09 holds a 2-byte number per measure, the measure numbers as SPP shows them, with a number repeated where a measure of a first ending repeats one; SPP Lite 5.1 does not keep them. The layout strings, the last thing before the notes, are not a section of this list (see Hairpins); so the notes block is still found by its shape.

Clef changes

After the texts: a record starting 02 (after ff ff), then per staff a length (6 per change), then per change ff ff and 4 bytes: the measure (2 bytes, from 1), the number of the note in it (from 1, rests count), the clef (the code of the SPP 5–7 format, 00 G, 01 F). In Bach's Toccata and Fugue: 02 | 00 00 | 00 0c | ff ff 00 11 01 01 ff ff 00 12 02 00 | 00 72 | ..., so no change in the first staff, then an F clef at the first note of measure 17 and a G clef at the second of measure 18 in the second. All 371 changes of 33 songs sit where SPP Lite 5.1 has them.

Notes block

Per staff record (each voice, see staff records), one block per measure: a 2-byte length, then the notes; then an empty block, ending the staff (the last staff's can be missing). An empty measure is an empty block too, and the empty measures after the last one with notes are left out: the staff record says how many measures are stored. The block is found by its shape; as the search finds its first note, the first staff's leading empty blocks lie before that and are counted back. Found in all 97 songs. With the stored counts, note counts and pitches agree with the resaved songs in 99.6 % of the notes.

Note

bytesmeaning
0fe
1the number of the note in the measure, from 1
2-3widths: they grow with the lyric syllable under the note
4the column in the measure
5the type
6the stem byte
7a rest only: 64
the tones, then optional records

Note type

The SPP 5–7 format's note types, rests 18 hex more, with one difference: the first of each three is dotted, where the SPP 5–7 format stores dots in a tag. So 07 is a dotted quarter, 08 a quarter, 09 a quarter triplet.

Stem byte

bitmeaning
01an f6 record follows the note (on about 47000 notes)
02beam starts
04beam ends
08the stem direction is set explicitly (son2emil's H^ up and H, down, with bit 20)
10tied to the next note: set on all 2256 tied notes of the resaved songs. A tied note has a byte per tone more after its tones, and that byte says what the tie of that tone is: 10 a tie to the next note (start), 20 one from the note before (stop), 30 both (continue). Checked on 3000 tones of the resaved songs: 100 %. A note can carry the flag with some tones only stopping, and a chord with only some tones tied
20stem down
40cue note, printed small: set on all 632 cue notes

Tones

A count, then the staff positions only, without MIDI pitch. From 14 up the count is 14 hex (20) + the number of tones, followed by 00, the positions, and one accidental per tone, with the SPP 5–7 format's codes: 02 ♭, 03 ♮, 04 ♯. Staff positions agree with the resaved songs in 99 % of the tones.

Pitch

As there is no MIDI pitch, it follows from the staff position, which gives the note name and octave, and an alteration: the last accidental at that position in the measure, or else the key signature. The written key is the concert key plus the staff's transposition. A tone tied from the note before keeps that note's pitch. This agrees with the MIDI pitch of the resaved songs in 99 % of the tones.

The missing pitch also shows in what SPP 5.1 makes of a tied-to tone: in some songs it saves MIDI pitch 0 for it.

Optional records

After the tones, fd, a bit mask, then the records the mask announces, always in the order of the table (seen in all combinations in the corpus). Lengths include the record's code byte:

bitrecordlengthmeaning
01e8?after the f6 record: a mask (bit 02 a dotted slur, in son2emil's (:) and records e7, e6, e5 (hide stem) and e3. Not read; on 5 notes of the corpus
02???
04f92 + 5 × the countthe ornaments and marks, laid out like the SPP 5–7 format's tag e5: a count, then 5 bytes per item, the first the mark
08fa or fb13crescendo or diminuendo: a hairpin, see Hairpins
10fc2 + per slur 9 + 4 × the pointsa count, then per slur a Bézier curve of 3 to 5 points (a pair of numbers per point, each in base 100 with an offset of about 1000, relative to the start note), then 4 pairs (high, low), the value high × 100 + low: a number, the measure where the slur ends (from 1, base 100 from measure 100 on: 03 2d is 345), the number of the end note in that measure (from 1; rests count), and a style minus 50 (32 03 is 3). The end is thus in the file, and no layout is needed. Of the slurs of the songs, 7395 starts and stops agree with SPP Lite 5.1's, 345 are only ours and 185 only SPP Lite's; where SPP Lite differs it is shifted, as with voltas and texts (Mozart's bassoon duet 5 by 4 measures)
20f82 + per verse 2 + its lengththe lyrics: a count of verses, then per verse a 2-byte length, 4 bytes (a place?) and the syllable; ~ is the hyphen
40–1one byte, 5b to 72; not a mark code (read as one, fewer marks agree); read by son2emil and not used

Read this way, the marks and lyrics of 96 % of the notes that have them agree with the resaved songs; most of the rest are one note off, in songs with several voices on a staff. The f6 record follows many notes, after the fd records or without them: see The f6 record.

Hairpins

The optional record fa (crescendo) or fb (diminuendo), 13 bytes: the code, then pairs (high, low) with the value (high − 50) × 100 + low: the start offset a6, a y, the length a2, another y, and two more. It is the same record as SPP 5's tag d1 (there with 100 instead of 50). A crescendo is on its first note, a diminuendo on its last. The end is not in the file: PriMus' son2emil works it out from the places of the notes, and so does the converter (hairpins.py), found by reading son2emil with Ghidra:

Against son2emil's output for all songs: 271 hairpins agree, 5 are only in son2emil's output, 7 only ours. Of the songs with hairpins, only the SPP 1.4 one has no layout strings that can be found. A measure can have a short string (no columns); those of the MEL songs are, and without allowing them the chain does not add up.

The f6 record

After the fd records (or the tones) comes f6 and a mask byte, found by reading son2emil; records in this order. Lengths include the code byte:

bitrecordlengthmeaning
01f52not understood
02f42the place in a multi-measure rest (see Multi-measure rests)
04f37the ottava code, then 5, the third perhaps where the line ends (see Ottavas)
08f25the first is 100 plus a jump up or down (son2emil writes JumpUp, JumpDown, JumpBack; perhaps a note drawn on the other staff)
10f15two pairs (high − 50) × 100 + low, an offset of something; son2emil reads them and does not use them (on 1155 notes)
20f06 + the length bytea chord symbol or text: two pairs (the place), a length byte, the text (son2emil turns " into maj and ! into m). No song of the corpus has one
40ef2 + its recordsa mask, then, each with its code byte (ee 5, a tuplet bracket; ed 2; ec 4, a grace note, the first of its 3 bytes says slashed; eb 2; ea 8; e9 2) for the bits 01, 02, 04, 08, 10 and 20; bit 02 of e5 is HideStem

Bit 01 of the stem byte says an f6 record follows (47000 notes). The 40 byte of the fd mask is read by son2emil and not used either.

The grace note: ec is there on all 369 grace notes SPP Lite 5.1 has and on no other note. The grace note keeps the duration of its type. After f6 may come e8, with the fd mask bit 01: a mask (bit 02 dotted slur, in son2emil's (:) and records e7, e6, e5 (hide stem) and e3; not read.

Multi-measure rests

Not one record as in SPP 5: each measure of the run has its rest, ending in the record f6 02 f4 NN, NN the place in the run (1, 2, 3), as in fe 01 00 08 01 1a 21 64 01 32 f6 02 f4 02. A run is the measures marked 1, 2, ... in every staff; the converter makes them one measure with the count, as SPP 5 has it. In Mozart's bassoon duet 5, the only song where SPP Lite 5.1 made multi-measure rests, all four (3, 2, 3 and 3 measures) are found exactly where it has them.

Ottavas

The record f6 04 f3 CC (or f6 14 f3 CC, followed by an f1 record) and 5 more bytes (00 46 00 00 00 on the first note of a line, zeros after) is on every note under an octave line, CC the code of the SPP 5–7 format's e2 (only 01 occurs). A rest in between has none. In Beethoven's Für Elise (15 notes) and Das Lied vom Traum (152 notes) every note under a line is where SPP Lite 5.1 has it, with the same pitch in all but one.