Skip to content

feat(importer): read TablEdit (.tef) files - #3013

Open
Danielku15 wants to merge 21 commits into
developfrom
feature/tabledit-importer
Open

Danielku15 wants to merge 21 commits into
developfrom
feature/tabledit-importer

Conversation

@Danielku15

Copy link
Copy Markdown
Member

Note

AI-authored disclosure (alphatab-ai-authored-v1)

Portions of this content were authored by an AI agent. The agent has read
AGENTS.md and the human submitter accepts responsibility for
compliance with the rules in that document.

Issues

Fixes #3006 (requested in discussion #2963)

Proposed changes

alphaTab can now load TablEdit files (.tef), saved by TablEdit 2.6x and later in the TablEdit 2 (formats 3–9) and
TablEdit 3 (formats 10–12) layouts. The new TablEditImporter is registered in the default importer list, so api.load,
ScoreLoader and the file pickers of the playground and the UI accept .tef files without further setup.

Why it is built this way. TablEdit does not store beats: it stores notes and markers on a 64th grid per string row. The
importer therefore rebuilds the alphaTab model from that grid instead of mapping records one to one:

  • TablEditImporter detects the format, reads the header, measures, instruments, chords, texts and lyrics, and rejects
    protected files (their notes are encrypted) and the oldest formats 1–2 with a hint to re-save in TablEdit 3.
  • TefBarBuilder turns the grid into voices and beats: notes of different length that sound together go to extra voices,
    tuplets and numbered connections are timed as TablEdit times them, gaps become rests, and effects, grace notes,
    ornaments, fingerings, strokes, markers (text, chord, accent, crescendo, fermata, slur, tempo, syncopation, patch,
    line break) and lyrics are attached to the beats of their track.
  • TefReadingList expresses TablEdit's reading list (the playback order) through repeats, alternate endings and
    D.C./D.S./coda/fine jumps, then checks the result against the order alphaTab's playback controller produces and logs a
    warning when the order cannot be expressed exactly.
  • TefFormat2Reader translates the completely different TablEdit 2 container into the geometry and cell layout of
    TablEdit 3, so both formats share the decoding above; TefBinary holds the shared byte and text access (UTF-8 with a
    Windows-1252 fallback for older texts).

Platform runtime. The importer and its tests run in C# and Kotlin too. That needed some runtime additions and fixes
outside the importer, each in its own commit:

  • fix(kotlin): DoubleList.spread() returned its spare capacity as extra zeros.
  • fix(csharp): Windows-1252 text was decoded as UTF-8 on .NET without a registered code pages provider (this also affects
    Guitar Pro 3–5 files read with importer.encoding = 'windows-1252').
  • fix(platform): Uint8Array.fill(value, start, end) (C#, Kotlin); in Kotlin a Uint8Array copy constructor,
    slice(start, end) on lists, includes/flatMap on number lists, sort() returning the list.
  • test(platform): the C# and Kotlin toEqual compare numbers and enum values by value as JavaScript does, Kotlin compares
    map entries by key and value; C# accepts a message for an expected throwing action, Kotlin has toBeDefined.

A separate style(importer) commit re-formats importer files touched by the formatter; it changes no behaviour.

Tests and how the expectations were obtained

  • test/importer/TablEditImporter.test.ts: 87 tests over 39 files entered in TablEdit 3.06 (plus patched copies for
    corrupt-file, reading-list-structure and protection cases). The expected values follow from what was entered in TablEdit
    and from the file format, not from importer output: notes, accidentals, ties, dynamics, voices, durations, tuplets and
    numbered connections, effects and parameters, grace notes and ornaments, fingering and strokes, instruments, clefs and
    drums, time and key signatures, pick-ups, markers, line breaks, lyrics, reading lists (including 16 reading-list
    structures checked against TablEdit's playback order) and corrupt or foreign data.
  • test/importer/TablEdit2Importer.test.ts: 20 tests over 6 TablEdit 2 files written byte by byte; each is compared with
    its conversion by TablEdit 3.06 (<name>.v3.tef), which serves as the oracle, plus explicit checks.
  • test/visualTests/features/TablEdit.test.ts: 4 visual tests (effects, tuplets, reading list with voltas, text and
    chord markers). The reference images are not part of this PR yet — they are under review.
  • The TablEdit files also run through the existing JSON, alphaTex exporter and serializer round trips.
  • During development the importer was additionally checked against TablEdit's own MIDI and MusicXML exports and against a
    local-only corpus of MuseScore's TablEdit test files (GPL, not added to the repository).

Results on the branch: TypeScript 2512 passed, C# 2372 passed, Kotlin 2372 passed; in each the only failures are the 4
TablEdit visual tests without reference images. npm run build-website passes.

Docs. New format page website/docs/formats/tabledit.mdx with a 154-row feature table for TablEdit 2 and 3 files
(model, reading, rendering, audio, alphaTex), new feature page features/import-load/tabledit.mdx, TablEdit added to the
format lists (README, introduction, feature pages, landing page, llms.txt, package description) and a release note in
releases/release1_10.mdx with a demo file.

Known limitations (all listed in the feature table):

  • Brushes and rolls cover the notes of one voice only; slides glide instead of stepping through the frets.
  • Dynamics are applied per beat (loudest note); accents and crescendos use alphaTab's fixed behaviour, not the stored
    volume changes; fermatas are not held longer.
  • No downward bends (pitch changes downward), no bend from a grace note, grace note durations come from their count;
    hammer-ons from another string are kept as slurs.
  • Not representable in alphaTab: chorus/reverb, hidden time signatures, single-string (banjo 5th string) capo, doubled
    courses and special tablature styles, diatonic/lettered tablature (pitches play correctly), thumb/finger strokes, brackets
    over notes, scale diagrams, extender lines, beats without stem or flag; notes and rests can only be hidden in all staves.
  • Clefs F3, C2 and C5 are shown as the nearest alphaTab clef; TablEdit drum characters are shown as a percussion staff.
  • Reading lists that repeats and jumps cannot express (for example playback not starting at the first bar) play the closest
    structure with a warning.
  • Unknown meaning in the format: some measure flags (ad lib, dotted/half bar lines), slide from/to nowhere, vibrato rates,
    some symbol numbers, beam-break variants, special characters, the rhythm-track option.
  • Not read: protected files (by design), TablEdit 1.x/early 2.x files (formats 1–2), TablEdit 2 drum and rhythm patterns
    (skipped with a warning), TablEdit 2 spacing and grace detail records.

Follow-ups (not in this PR)

  • F1 Brush/roll extent across all simultaneous beats (rendering and MIDI)
  • F2 Printed fret labels for non-chromatic necks
  • F3 Stepped vs. continuous slide playback
  • F4 Clefs F3, C2, C5
  • F5 Explicit playback order for reading lists that repeats cannot express
  • F6 Per-note dynamics/velocity
  • F7 Reverb/chorus
  • F8 Drum-tab display with characters

Open point before merge: permission from the TablEdit authors to ship the .tef test files and the website demo file,
made with the TablEdit demo, under MPL-2.0 is still pending.

Checklist

  • I consent that this change becomes part of alphaTab under its current or any future open source license
  • This PR is linked to an accepted issue (see above)
  • Changes are implemented
  • New tests were added
  • I have read AGENTS.md if an AI helped draft any part of this PR

AI authorship disclosure

  • No AI agent authored any part of this PR (description, code, tests, or commit messages)
  • An AI agent contributed to this PR. The AI-authored disclosure block
    (alphatab-ai-authored-v1) is present at the top of this body, and I have
    personally reviewed every change and can explain each one

Further details

  • This is a breaking change
  • Docs / release notes updated in website/

Covers the primary and combination effects, the second-column effects,
harmonics, bends to the tied target note, pitch changes, grace notes with
their extension records, left and right hand fingerings, pick strokes and
ottava attributes.
Reads the text and chord tables and applies text, chord diagram, accent,
crescendo, fermata, slur and MIDI patch markers to the beats of their
track, plus tempo changes with ramps, syncopation as triplet feel, line
breaks and double bars.
…s reading of it

Corrections found with files entered in TablEdit:
- the instrument bank is the bank MSB
- transposing instruments show their written key
- chord diagrams take the string count of the instrument they are used on
- a reading list with an unknown record size is ignored, and repeat signs
  are only taken when the file has no reading list
- artificial harmonics use the stored harmonic fret, also from the
  combination byte; natural harmonics get the harmonic value of their fret
- grace notes keep their stored fret, also from another string, and a
  hammer-on grace from another string keeps its slur
- trills use the stored trill fret
- bends and the tremolo bar take their amount from the next note on the
  string, tied or not
- mordents and turns take their direction from their auxiliary notes
- the ghost combination with the ghost effect means no effect
- crescendos without length or amount are ignored
- accents apply to the notes starting at the marker
- gaps after dotted 64ths and incomplete tuplets are filled with 1/128 or
  tuplet rests
- only the first lyrics section of a module is used

Additions:
- tremolo bar up/down and dive-return as whammy bar
- roll and brush speed as brush duration (bits 4-6, 5 TablEdit ticks per
  step)
- excluded-from-beaming notes and secondary beam breaks on rests; free
  bars are beamed across the bar or its halves
- hidden notes and rests where no other shown staff displays them
- measures per line from line-break markers
- numbered connections as n-tuplets of n : m/2, timed like TablEdit
- standalone pick strokes
- the let-ring instrument option
- chord names in lyrics and lyric text markers
- effect parameters stored in the extension record
TablEdit 2.6x and later 2.x versions save .tef files in formats 3 to 9.
Their container has nothing in common with the TablEdit 3 one: a fixed
header with counts instead of a pointer table, sequential blocks, 6-byte
grid records addressed per measure slot, measures of the header's
longest size whose shorter variants are right-aligned and announced by a
cell, keys in the header or a per-measure block, and partly different
cell codes. The records inside (instruments, chords, texts) and the
meaning of the note bytes are the ones of TablEdit 3.

So TablEdit 2 gets its own container reader, TefFormat2Reader, which
translates the measures and the grid into the measure geometry and cell
layout the TablEdit 3 path produces. The importer keeps one entry point,
detects the format and hands both paths to the shared instrument and
chord record decoding, TefBarBuilder and TefReadingList, which stay
unchanged. Byte access and text decoding move into TefBinary for both
readers.

Formats 1 and 2 (TablEdit 1.x and early 2.x) are still rejected with a
hint to re-save the file with TablEdit 3. Protected TablEdit 2 files are
rejected like protected TablEdit 3 files. Drum and rhythm patterns of
TablEdit 2 are skipped with a warning.
TablEdit keeps the abbreviation at byte 26 of the instrument name,
introduced by '^' (a name entered as "Lead Guitar^Ld" is stored as
"Lead Guitar", leftovers, "^Ld"); the bytes between the end of the name
and byte 26 are leftovers of earlier names. A '^' within the name is part
of the name, as TablEdit 3 shows it for TablEdit 2 files.
…it importer

Import order and line wrapping as the formatter writes them; no change in
behaviour.
… jumps

Derives repeats with pass counts, alternate endings and D.C./D.S. jumps
with coda and fine targets from the reading list, checks the resulting
playback order, turns labels into sections, and shows the graphical
repeat signs when a file has no reading list.

A repeat pass that continues past the repeat (1-2, 1-2, 1-2, 3-n) counts
as a pass of the repeat instead of a D.C., and a second D.S. that skips
ahead again ends at the double coda (D.S.S. al Double Coda).
…lEdit

Files entered in TablEdit 3.06 (and some patched copies of them); each
test asserts the entered values per variant: notes, accidentals, ties,
dynamics, voices, durations and tuplets, numbered connections, effects
and their parameters, grace notes and ornaments, fingering and strokes,
attributes, instruments, clefs and drums, time and key signatures,
pick-up and flag bits, text, chord, tempo, syncopation and patch markers,
line breaks, lyrics and reading lists. The reading list structures and
the corrupt file cases patch these files. The tests stay transpilable to
C# and Kotlin.
The files in test-data/tabledit/v2 are TablEdit 2 files (format 3) written
byte by byte; each <name>.v3.tef is the same file opened in TablEdit 3.06
and saved again. The TablEdit 2 import must equal the import of the
conversion, plus explicit checks of notes, effects, markers, measures,
instruments, the skipped drum and rhythm patterns and protected files.
…ON, alphaTex and the serializer

Visual tests for effects, tuplets, a reading list with voltas and text
and chord markers; the TablEdit files also run through the JSON, alphaTex
and serializer round trips.
A format page with the feature table of TablEdit 2 and 3 files and their
support in alphaTab, a feature page for loading TablEdit files, TablEdit
in the lists of supported formats and a release note.
.NET only knows Windows-1252 when the host registers the code pages
provider; without it the decoder fell back to UTF-8 and replaced every
accented character of a Windows-1252 text.
… tests to the C# and Kotlin runtimes

- Uint8Array.fill with a start and end (C#, Kotlin)
- a Uint8Array copied from another Uint8Array (Kotlin)
- slice with a start and end on lists and boolean lists (Kotlin)
- includes and flatMap on number lists (Kotlin)
- sort without a comparison returns the sorted list (Kotlin)
…e in JavaScript

In the C# and Kotlin test globals, list elements that are numbers or
enum values are compared by their numeric value, and Kotlin compares map
entries by key and value. C# accepts a message for an expected throwing
action, Kotlin has toBeDefined.
Detects TablEdit files, reads title fields, measures, instruments and the
note grid, and lays the grid out into alphaTab voices with packed tuplets,
lanes for overlapping lengths and rest filling.

Triplet runs are spread relative to the beat of their first member, other
tuplets relative to their beat, as TablEdit times them. Measure sizes
follow the time signature, 12-byte measure records and formats 11 and 12
are recognised, and texts that are not valid UTF-8 are read as
Windows-1252.
effects-b5.tef is re-entered in TablEdit with the notes after the two
tremolo bars in bar 2 on frets 3 and 7, so the up/down and the
dive-return take an amount from the next note on the string. A patched
copy keeps the case of a target on the same fret, which has no amount.
The format page notes that a tremolo bar without an amount is not shown.
@Danielku15 Danielku15 self-assigned this Nov 6, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Support for TablEdit files

1 participant