Repository navigation
fix: align beaming, metronome, count-in and automations with pick-up bars; fix beat timers and multi-track MIDI events - #2939
Merged
Conversation
The beats of a pick-up bar are stored from tick 0, but musically they form the end of a full bar. Beaming and triplet feel interpreted the bar relative ticks as if the bar started on the downbeat, e.g. a 3/8 pick-up in 2/4 was beamed (C D) E instead of C (D E) and swung the offbeat eighth. MasterBar now caches the offset of the pick-up within the nominal meter (computed once in finish) which both features add to the bar relative position. Refs #2871
…nature Automation ratio positions are written relative to the full duration of the time signature (Guitar Pro stores 0.5 for an automation on the 3rd eighth note of a 3/8 pick-up in 2/4, the Gp/MusicXML/Capella importers follow this). The MIDI generation converted them using the shorter actual pick-up duration, playing tempo changes and sync points inside pick-ups too early (e.g. tick 720 instead of 960). The alphaTex importer wrote sustain pedal positions with the actual duration. All conversions now use one internal helper in ModelUtils, the convention is documented on Automation.ratioPosition. Refs #2871
…rough Beat timers were calculated from a running time which every staff advanced by a whole bar. With multiple tracks or staves the timers accumulated (two tracks showed 0/4000/8000ms and 2000/6000/10000ms instead of 0/2000/4000ms). The bar end recalculation additionally compared a bar relative tempo automation tick with an absolute tick, breaking the timers of all bars following a mid-bar tempo change. All staves now start at the bar start time which the play-through already tracks correctly respecting tempo changes. Refs #2871
The metronome was generated by the sequencer on one continuous grid derived from the time signature events. A midi time signature cannot express the position of a pick-up within the meter, after a 3/8 pick-up in 2/4 every click fell on an offbeat for the whole song. The click counters were computed from the absolute tick and were wrong after time signature changes. The count-in always counted a single bar, letting pick-ups or mid-bar starts enter as if they were a downbeat. The MidiFileGenerator now emits the clicks bar by bar (respecting repeats and pick-ups) via the new IMidiFileHandler.addMetronome. They are not written to standard midi files. The sequencer uses these clicks and keeps the time signature based grid for midi files without clicks. The count-in counts one full bar and continues until the beat on which the playback starts (e.g. 3 clicks before a 3/8 pick-up in 2/4, matching Guitar Pro and MuseScore). Refs #2871
MidiFile.events called itself recursively for files in the multi-track format, overflowing the stack (e.g. when loading such a file into the player) and would have returned an empty list. The already sorted tracks are now merged, which keeps the order of events on the same tick on all platforms (the sort used before is not stable in the C# port). Refs #2871
leocaseiro
added a commit
to leocaseiro/alphaTab
that referenced
this pull request
Oct 13, 2026
…erLine#2928 and CoderLine#2939 (F-47) The next alpha upgrade's rebase brings two merged upstream changes that Task 26 Step 4 did not name. CoderLine#2928 adds a test worker that Task 5's 'error' overload breaks at typecheck (one line). CoderLine#2939 merges without a conflict, but its start-beat distance is in the song's milliseconds while a count-in that follows the media plays at the media's tempo, so an off-beat start leads into the song off its beat when the recording's tempo differs from the score's. Step 4 now names both: widen TestSynthWorker's addEventListener, scale the distance by the song's tempo over the count-in's (two lines) with a FollowMedia test that expects 600 ms, not 650, and update spec §2's count-in row and S6 at rebase time. The base stays 25ef76d; the newest alpha on npm (1.9.0-alpha.1891) is still older than it. Decided in lap 3's triage (option 2 of 3). Spiked on develop plus Task 2: the new test fails at 650 ms and passes at 600 ms; upstream's count-in tests 16/16, Task 2's 7/7, tsc clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 #2871
Proposed changes
#2871: in a pick-up bar (anacrusis) the beaming is wrong, and the metronome and count-in are out of sync. alphaTab stores a pick-up's beats from tick 0, and the bar plays only for its content length. Musically, though, a pick-up is the end of a full bar. No layer knew about that shift:
Analysing the issue turned up more defects in the same layers, so this PR fixes all of them.
Design
MasterBarcaches the pick-up's position within the full bar of its time signature: the full bar duration minus the actual duration. It is computed once infinish()and is internal (not part of the public API or JSON). Beaming, triplet feel and the metronome add it to positions inside the bar. One offset per master bar is enough, because Guitar Pro also counts every voice from the bar start.Automation.ratioPositionis relative to the full time signature duration, also in pick-ups. Guitar Pro 8 writes0.5for a tempo change on the 3rd eighth of a 3-eighth pick-up in 2/4, and the GP, MusicXML and Capella importers already follow this. All conversions now go through one internalModelUtilshelper.MidiFileGeneratoremits the clicks bar by bar through the newIMidiFileHandler.addMetronome, so repeats, jumps, time-signature changes and pick-ups are resolved where the bars are known.AlphaSynthMidiFileHandleradds them asAlphaTabMetronomeEvents, but not in SMF1 mode, so exported MIDI files are unchanged.Behaviour changes
Reference score:
\ts 2 4 \ac C4.8 D4.8 E4.8 | F4.4 G4.4 | …at 120 bpm.0:0 960:1 1920:0 2880:1 …, so every click after the pick-up falls on an off-beat480:1 1440:0 2400:1 3360:0 …3,0,1,20,1,2,30/4000/8000, track B2000/6000/10000ms0/2000/4000ms for both tracksMidiFile.eventswithMidiFileFormat.MultiTrackNotes:
metronomeNumerator) are only visible tomidiEventsPlayedconsumers. The built-in synthesizer plays every click the same.MidiFile.events: the getter called itself instead of filling its local list. The tracks are now merged instead of sorted, because the C# port's sort is not stable.Not changed (deliberately)
API changes
IMidiFileHandler.addMetronome(tick, counter, durationInTicks)is a new required member. Custom handler implementations need to add it; they can ignore the call. The same happened withaddTickShiftin fix(player): grace beats on song start #2415.AlphaTabMetronomeEventandAutomation.ratioPosition.Tests
MidiFileGenerator.test.ts:MidiFile.eventsSyncPoint.test.ts: count-in at a bar start, before a pick-up and mid-barAlphaTexImporter.test.ts: sustain-pedal position in a pick-upspecial-notes/beaming-anacrusisTypeScript, C# and Kotlin builds and test suites pass.
Checklist
AI authorship disclosure
(
alphatab-ai-authored-v1) is present at the top of this body, and I havepersonally reviewed every change and can explain each one
Further details