Guide for implementers · Normative requirements remain in the versioned specification
The reader has two independent lyric sources
An OLA MIDI file is still a Standard MIDI File. It carries a complete OLA JSON document in Sequencer-Specific meta events (FF 7F) and a separately generated primary lyric stream in standard Lyric meta events (FF 05).
Treat the embedded document as authority only after its envelope, JSON, semantic model, SMF profile, and target bindings have been checked. If it is missing, corrupt, unsupported, or ambiguous, the MIDI file remains usable: fall back to FF 05.
Canonical OLA-SMF 0.1 uses 00 4C 41, the memorable “0LA” community value, followed by OLA1. The three-byte value occupies the MIDI manufacturer-ID slot but is deliberately unregistered. It is not assigned or endorsed by the MIDI Association. Never identify OLA from the namespace alone; require the magic and validate the complete envelope.
Quick start with the repository tool
From the Keyfire Helicon repository root, migrate ordinary standard lyrics into an OLA-SMF file and then verify the result:
dotnet run --project tools/KeyfireHelicon.Tools.Ola -- \
migrate-standard --input input.mid --output output.mid
dotnet run --project tools/KeyfireHelicon.Tools.Ola -- \
verify --input output.mid
migrate-standard imports only ordinary FF 05 lyrics. It produces a coarse projection because an importer cannot honestly infer syllables merely from a sequence of events. It does not interpret either obsolete Keyfire lyric manifest as OLA.
The tool is a migration and verification utility, not a full authoring interface. Use the reference libraries when you need to create lanes, split cues, attach notes, set hard anchors, or reconcile an external edit.
Recognize a candidate, then prove it
In each FF 7F event, inspect the event data—not the complete MIDI event encoding. A canonical candidate starts with these bytes:
00 4C 41 4F 4C 41 31
community ASCII “OLA1”
namespace magic
The namespace is outside the 88-byte OLA header, so header offset zero is the first byte of OLA1. A prefix match makes an event a candidate only. A reader must still validate the wire version, message type, codec, flags, header length, document identity, chunk set, UTF-8, JSON, and binding evidence.
Reader procedure
- Collect candidates.Read every track without assuming same-tick event order. Select Sequencer-Specific events whose data has a syntactically valid manufacturer namespace followed immediately by
OLA1. - Parse bounded headers.Reject unsupported wire versions, message types, codecs, nonzero flags, wrong header lengths, zero chunk counts, out-of-range indexes, or declared sizes beyond your supported limits.
- Group complete sets.Group by manufacturer namespace, document UUID, revision, and decoded hash. Every chunk in a set must be on one track and must agree on every header field except index and data.
- Assemble exactly once.Require each index from zero through
chunkCount - 1exactly once. Order by index, concatenate chunk data, and verify encoded and decoded lengths. - Authenticate the payload bytes.For codec 0, encoded and decoded bytes are identical. Verify the SHA-256 before decoding strict UTF-8. Reject a BOM, malformed UTF-8, duplicate properties, comments, or trailing commas.
- Validate the OLA document.Require
format: "open-lyric-alignment",version: "0.1", JSON Schema validity, and the semantic checks that a schema cannot express: globally unique IDs, resolved references, lane ownership, units, ranges, anchors, and limits. - Bind document to envelope.The JSON
documentIdmust be the same RFC 4122 UUID as the header; its canonical decimal-stringrevisionmust equal the unsigned header revision. - Select without guessing.The greatest valid revision of one UUID is current. Different UUIDs or equally current payloads are ambiguous unless exactly one candidate matches the SMF binding hashes. Never merge candidate documents.
- Verify the SMF profile.Require one SMF target, tick units, matching positive PPQ, exactly one primary lane with an alignment set for that target, and all three binding hashes.
- Report a useful state.Return the document, per-binding results, and diagnostics. If no authoritative document survives, use standard
FF 05lyrics rather than failing the MIDI file.
A conforming reader supports at least 1,024 chunks of at most 4,096 data bytes each, matching the 4 MiB OLA JSON ceiling. Apply bounds before allocating. Lower operational limits must fail visibly; they must not discard part of a document.
The exact 88-byte chunk header
All unsigned integers are big-endian. Offsets begin after the three namespace bytes.
| Offset | Size | Field | Value or meaning |
|---|---|---|---|
| 0 | 4 | Magic | OLA1 / 4F 4C 41 31 |
| 4 | 1 | Wire major | 00 |
| 5 | 1 | Wire minor | 01 |
| 6 | 1 | Message type | 01 document chunk |
| 7 | 1 | Codec | 00 raw UTF-8 JSON |
| 8 | 2 | Flags | 0000; reserved |
| 10 | 2 | Header length | 0058 / 88 |
| 12 | 16 | Document UUID | RFC 4122 network-order bytes |
| 28 | 4 | Revision | Unsigned OLA revision |
| 32 | 4 | Chunk index | Zero-based |
| 36 | 4 | Chunk count | Total, at least one |
| 40 | 8 | Encoded length | All concatenated chunk-data bytes |
| 48 | 8 | Decoded length | Decoded JSON byte length |
| 56 | 32 | Decoded SHA-256 | Hash of exact decoded JSON bytes |
| 88 | Variable | Chunk data | At most 4,096 bytes |
Writer procedure
- Start with valid semantics.Create at least one lane, one line per lane, and one non-empty cue per line. Keep lyric text, separators, voices, placements, and target timing in their proper parts of the model.
- Satisfy the SMF profile.Use an RFC 4122 UUID document ID, revision 0–4,294,967,295, exactly one primary lane, and one
kind: "smf"target withtimeUnit: "tick"and the file PPQ. - Advance semantic revisions.Increase
revisionwhenever meaningful content changes. Reformatting byte-identical values does not require a semantic revision, but it still changes the payload hash and envelope. - Generate the projection first.Project the primary lane into
FF 05. Use strict RP-017 only when every requirement is satisfied; otherwise emit a documented coarse projection without inventing syllables. - Compute all three bindings.Hash normalized note topology, the tempo map, and the exact generated primary lyric projection. Store lowercase SHA-256 values on the SMF target.
- Serialize strict JSON.Write UTF-8 without a BOM. Preserve unknown additive properties and URI-namespaced extensions. Write every 64-bit OLA value as a canonical decimal JSON string.
- Build the envelope.Hash the exact JSON bytes, split them into slices no larger than 4,096 bytes, and give every chunk an identical header except for index and chunk data.
- Place data without moving music.Put chunks at absolute tick zero. For Type 1, append a track named
Open Lyric Alignmentand put the primary projection there. For Type 0, use the sole track and do not change the file to Type 1 merely for OLA. - Preserve foreign data.Keep unrelated
FF 7Fevents byte-for-byte. Replace only OLA events in the namespace you are authorized to write. Refuse to take overOLA1data found under another namespace. - Save atomically and verify.Write to a sibling temporary file, replace only after success, reopen the output, and require a usable document with matching note, tempo, and lyric bindings.
Choose the honest compatibility projection
| Profile | Use it when | Guarantee |
|---|---|---|
| OLA-RP017-Portable-Syllabic-0.1 | Every cue is a syllable with explicit word position, matching separators, and printable ASCII text or an author-supplied fallback. | One portable lyric event per syllable placement, with defined melisma and CR/LF boundary behavior. |
| OLA-FF05-Coarse-0.1 | The source has phrase, word, line, uncertain, or nonportable text timing. | Known starts remain useful, while exact Unicode and richer structure stay lossless in OLA JSON. |
Never silently transliterate, replace unsupported characters with ?, infer syllables from event count, or claim RP-017 conformance for coarse data. A strict projection uses standalone CR and LF events for line and paragraph boundaries; empty OLA cues are invalid.
Use the bindings to classify edits
| Payload | Notes | Tempo | Projection | Required behavior |
|---|---|---|---|---|
| Valid | Match | Match | Match | Use OLA as authority. |
| Valid | Match | Match | Changed | Preserve both lyric versions; reconcile before overwrite. |
| Valid | Changed | Any | Match | Mark note links stale; retain ranges and rebind conservatively. |
| Valid | Match | Changed | Match | Keep tick placements; recompute real-time views and mappings. |
| Valid | Changed | Any | Changed | Treat as broadly edited; do not silently prefer either source. |
| Unavailable | Any | Any | Available | Ignore OLA authority and use FF 05. |
A missing hash means “unverified,” not “match.” After explicit reconciliation, regenerate the JSON bindings, chunks, and primary projection as one save.
Use the C# reference implementation
The dependency-free OpenLyricAlignment library implements the model, strict JSON codec, limits, and semantic validator. OpenLyricAlignment.Smf adds the MIDI envelope, reader, writer, projector, fingerprints, standard-lyric importer, and conservative note-link resolver. Both libraries are MIT-licensed.
Read and inspect
using Melanchall.DryWetMidi.Core;
using OpenLyricAlignment.Smf;
var midi = MidiFile.Read(
"song.mid",
OlaSmfWriter.CreateCompatibleReadingSettings());
var result = OlaSmfReader.Read(midi);
if (result.IsUsable &&
result.BindingStatus == OlaSmfBindingStatus.Match)
{
var document = result.Document!;
// Use the validated lanes, cues, placements, and anchors.
}
else
{
// Preserve diagnostics and fall back to standard FF 05 lyrics.
}
The OLA reader deliberately does not turn ordinary lyrics into an OLA document implicitly. If your workflow explicitly imports standard lyrics, call OlaStandardLyricImporter.Import(midi) and retain the importer’s coarse semantics.
Write or update atomically
using OpenLyricAlignment.Smf;
var updated = document with
{
Revision = checked(document.Revision + 1),
};
var write = OlaSmfWriter.WriteAtomic(
"song.mid",
updated,
new OlaSmfWriteOptions
{
ProjectionProfile = OlaLyricProjectionProfile.CoarseUtf8,
});
Console.WriteLine($"{write.DocumentChunkCount} OLA chunks");
Console.WriteLine($"{write.ProjectedLyricEventCount} lyric events");
Use StrictRp017Ascii only for a document that satisfies the strict syllabic profile. By default, the writer refuses unresolved external edits, an unadvanced changed revision, another OLA namespace, unsafe private-ID output, or destructive whole-file lyric replacement that was not explicitly requested.
Minimum interoperability checklist
- Parse OLA candidates only from
FF 7F; never confuse legacy Keyfire byte strings withOLA1. - Require the namespace, magic, exact 88-byte header, complete bounded chunk set, payload SHA-256, strict UTF-8, JSON validation, and semantic validation.
- Keep wire version and JSON model version independent.
- Check document UUID and revision against the envelope.
- Verify note topology, tempo map, and lyric projection separately.
- Retain resolved ranges when note anchors cannot be safely rebound.
- Never choose an ambiguous chord member for a hard anchor.
- Emit one primary standard lyric projection and preserve foreign MIDI events.
- Fall back to
FF 05when OLA is unavailable; do not make the MIDI unusable. - Test valid, schema-invalid, semantic-invalid, corrupt, incomplete, unsupported, ambiguous, and externally edited files.
Normative source
Implement against the specification, test against the fixtures.
This guide explains the contract but does not replace the capitalized requirements, exact hash record formats, resource limits, schema, and conformance fixtures in the OLA 0.1 specification set.