OLA-SMF 0.1 · Implementation guide

Read and write OLA MIDI.

A practical path from an ordinary .mid file to a validated OLA document—and back—without losing compatibility data or hiding external edits.

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.

Namespace status

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

  1. 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.
  2. 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.
  3. 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.
  4. Assemble exactly once.Require each index from zero through chunkCount - 1 exactly once. Order by index, concatenate chunk data, and verify encoded and decoded lengths.
  5. 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.
  6. 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.
  7. Bind document to envelope.The JSON documentId must be the same RFC 4122 UUID as the header; its canonical decimal-string revision must equal the unsigned header revision.
  8. 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.
  9. 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.
  10. Report a useful state.Return the document, per-binding results, and diagnostics. If no authoritative document survives, use standard FF 05 lyrics rather than failing the MIDI file.
Resource floor

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.

All unsigned integers are big-endian. Offsets begin after the three namespace bytes.

OffsetSizeFieldValue or meaning
04MagicOLA1 / 4F 4C 41 31
41Wire major00
51Wire minor01
61Message type01 document chunk
71Codec00 raw UTF-8 JSON
82Flags0000; reserved
102Header length0058 / 88
1216Document UUIDRFC 4122 network-order bytes
284RevisionUnsigned OLA revision
324Chunk indexZero-based
364Chunk countTotal, at least one
408Encoded lengthAll concatenated chunk-data bytes
488Decoded lengthDecoded JSON byte length
5632Decoded SHA-256Hash of exact decoded JSON bytes
88VariableChunk dataAt most 4,096 bytes

Writer procedure

  1. 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.
  2. 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 with timeUnit: "tick" and the file PPQ.
  3. Advance semantic revisions.Increase revision whenever meaningful content changes. Reformatting byte-identical values does not require a semantic revision, but it still changes the payload hash and envelope.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. Place data without moving music.Put chunks at absolute tick zero. For Type 1, append a track named Open Lyric Alignment and put the primary projection there. For Type 0, use the sole track and do not change the file to Type 1 merely for OLA.
  9. Preserve foreign data.Keep unrelated FF 7F events byte-for-byte. Replace only OLA events in the namespace you are authorized to write. Refuse to take over OLA1 data found under another namespace.
  10. 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

ProfileUse it whenGuarantee
OLA-RP017-Portable-Syllabic-0.1Every 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.1The 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

PayloadNotesTempoProjectionRequired behavior
ValidMatchMatchMatchUse OLA as authority.
ValidMatchMatchChangedPreserve both lyric versions; reconcile before overwrite.
ValidChangedAnyMatchMark note links stale; retain ranges and rebind conservatively.
ValidMatchChangedMatchKeep tick placements; recompute real-time views and mappings.
ValidChangedAnyChangedTreat as broadly edited; do not silently prefer either source.
UnavailableAnyAnyAvailableIgnore 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 with OLA1.
  • 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 05 when 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.