diff --git a/Source/AlphaTab/AlphaTabApi.cs b/Source/AlphaTab/AlphaTabApi.cs index 56c0902ef..3bec2f0c8 100644 --- a/Source/AlphaTab/AlphaTabApi.cs +++ b/Source/AlphaTab/AlphaTabApi.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using AlphaTab.Audio; using AlphaTab.Audio.Generator; using AlphaTab.Audio.Synth; @@ -916,7 +917,7 @@ private void CursorUpdateTick(int tick, bool stop = false) var beat = cache.FindBeat(tracks, tick); if (beat != null) { - CursorUpdateBeat(beat.CurrentBeat, beat.NextBeat, beat.Duration, stop); + CursorUpdateBeat(beat.CurrentBeat, beat.NextBeat, beat.Duration, stop, beat.BeatsToHighlight); } } } @@ -926,7 +927,12 @@ private void CursorUpdateTick(int tick, bool stop = false) /// /// updates the cursors to highlight the specified beat /// - private void CursorUpdateBeat(Beat beat, Beat nextBeat, double duration, bool stop) + private void CursorUpdateBeat( + Beat beat, + Beat nextBeat, + double duration, + bool stop, + FastList beatsToHighlight = null) { if (beat == null) { @@ -983,8 +989,14 @@ private void CursorUpdateBeat(Beat beat, Beat nextBeat, double duration, bool st if (!stop) { - var className = BeatContainerGlyph.GetGroupId(beat); - UiFacade.HighlightElements(className); + if (beatsToHighlight != null) + { + foreach (var highlight in beatsToHighlight) + { + var className = BeatContainerGlyph.GetGroupId(highlight); + UiFacade.HighlightElements(className); + } + } var nextBeatX = barBoundings.VisualBounds.X + barBoundings.VisualBounds.W; // get position of next beat on same stavegroup diff --git a/Source/AlphaTab/Audio/Generator/MidiFileGenerator.cs b/Source/AlphaTab/Audio/Generator/MidiFileGenerator.cs index 4255f1086..2584b5861 100644 --- a/Source/AlphaTab/Audio/Generator/MidiFileGenerator.cs +++ b/Source/AlphaTab/Audio/Generator/MidiFileGenerator.cs @@ -266,6 +266,7 @@ private void GenerateBeat(Beat beat, int barStartTick, Bar realBar) ? audioDuration : beat.NextBeat.AbsolutePlaybackStart - beat.AbsolutePlaybackStart; beatLookup.End = barStartTick + beatStart; + beatLookup.HighlightBeat(beat); beatLookup.End += realTickOffset > audioDuration ? realTickOffset : audioDuration; diff --git a/Source/AlphaTab/Audio/MidiTickLookup.cs b/Source/AlphaTab/Audio/MidiTickLookup.cs index 4111646bc..12106b5d9 100644 --- a/Source/AlphaTab/Audio/MidiTickLookup.cs +++ b/Source/AlphaTab/Audio/MidiTickLookup.cs @@ -8,13 +8,15 @@ namespace AlphaTab.Audio /// public class BeatTickLookup { + private FastDictionary _highlightedBeats; + /// - /// Gets or sets the start time in midi ticks at which the given beat is played. + /// Gets or sets the start time in midi ticks at which the given beat is played. /// public int Start { get; set; } /// - /// Gets or sets the end time in midi ticks at which the given beat is played. + /// Gets or sets the end time in midi ticks at which the given beat is played. /// public int End { get; set; } @@ -24,9 +26,30 @@ public class BeatTickLookup public Beat Beat { get; set; } /// - /// Gets or sets whether the beat is the placeholder beat for an empty bar. + /// Gets or sets whether the beat is the placeholder beat for an empty bar. /// public bool IsEmptyBar { get; set; } + + /// + /// Gets or sets a list of all beats that should be highlighted when + /// the beat of this lookup starts playing. + /// + public FastList BeatsToHighlight { get; set; } + + public BeatTickLookup() + { + _highlightedBeats = new FastDictionary(); + BeatsToHighlight = new FastList(); + } + + public void HighlightBeat(Beat beat) + { + if (!_highlightedBeats.ContainsKey(beat.Id)) + { + _highlightedBeats[beat.Id] = true; + BeatsToHighlight.Add(beat); + } + } } /// @@ -35,12 +58,12 @@ public class BeatTickLookup public class MasterBarTickLookup { /// - /// Gets or sets the start time in midi ticks at which the MasterBar is played. + /// Gets or sets the start time in midi ticks at which the MasterBar is played. /// public int Start { get; set; } /// - /// Gets or sets the end time in midi ticks at which the MasterBar is played. + /// Gets or sets the end time in midi ticks at which the MasterBar is played. /// public int End { get; set; } @@ -50,13 +73,13 @@ public class MasterBarTickLookup public int Tempo { get; set; } /// - /// Gets or sets the MasterBar which is played. + /// Gets or sets the MasterBar which is played. /// public MasterBar MasterBar { get; set; } /// /// Gets or sets the list of object which define the durations - /// for all played within the period of this MasterBar. + /// for all played within the period of this MasterBar. /// public FastList Beats { get; set; } @@ -75,7 +98,7 @@ public MasterBarTickLookup() } /// - /// Performs the neccessary finalization steps after all information was written. + /// Performs the neccessary finalization steps after all information was written. /// public void Finish() { @@ -83,7 +106,7 @@ public void Finish() } /// - /// Adds a new to the list of played beats during this MasterBar period. + /// Adds a new to the list of played beats during this MasterBar period. /// /// public void AddBeat(BeatTickLookup beat) @@ -93,29 +116,34 @@ public void AddBeat(BeatTickLookup beat) } /// - /// Represents the results of searching the currently played beat. + /// Represents the results of searching the currently played beat. /// /// public class MidiTickLookupFindBeatResult { /// - /// Gets or sets the beat that is currently played. + /// Gets or sets the beat that is currently played. /// public Beat CurrentBeat { get; set; } /// - /// Gets or sets the beat that will be played next. + /// Gets or sets the beat that will be played next. /// public Beat NextBeat { get; set; } /// - /// Gets or sets the duration in milliseconds how long this beat is playing. + /// Gets or sets the duration in milliseconds how long this beat is playing. /// public int Duration { get; set; } + + /// + /// Gets or sets the beats ot highlight along the current beat. + /// + public FastList BeatsToHighlight { get; set; } } /// - /// This class holds all information about when s and s are played. + /// This class holds all information about when s and s are played. /// public class MidiTickLookup { @@ -130,7 +158,7 @@ public class MidiTickLookup public FastDictionary MasterBarLookup { get; } /// - /// Gets a list of all sorted by time. + /// Gets a list of all sorted by time. /// public FastList MasterBars { get; } @@ -144,11 +172,12 @@ public MidiTickLookup() } /// - /// Performs the neccessary finalization steps after all information was written. + /// Performs the neccessary finalization steps after all information was written. /// public void Finish() { MasterBarTickLookup previous = null; + var activeBeats = new FastList(); foreach (var bar in MasterBars) { bar.Finish(); @@ -157,12 +186,36 @@ public void Finish() previous.NextMasterBar = bar; } + foreach (var beat in bar.Beats) + { + // 1. calculate newly which beats are still active + var newActiveBeats = new FastList(); + // TODO: only create new list if current position changed + foreach (var activeBeat in activeBeats) + { + if (activeBeat.End > beat.Start) + { + newActiveBeats.Add(activeBeat); + // 2. remember for current beat which active beats to highlight + beat.HighlightBeat(activeBeat.Beat); + // 3. ensure that active beat highlights current beat if they match the range + if (beat.Start <= activeBeat.Start) + { + activeBeat.HighlightBeat(beat.Beat); + } + } + } + + newActiveBeats.Add(beat); + activeBeats = newActiveBeats; + } + previous = bar; } } /// - /// Finds the currently played beat given a list of tracks and the current time. + /// Finds the currently played beat given a list of tracks and the current time. /// /// The tracks in which to search the played beat for. /// The current time in midi ticks. @@ -253,6 +306,7 @@ public MidiTickLookupFindBeatResult FindBeat(Track[] tracks, int tick) result.Duration = nextBeat == null ? MidiUtils.TicksToMillis(beat.End - beat.Start, masterBar.Tempo) : MidiUtils.TicksToMillis(nextBeat.Start - beat.Start, masterBar.Tempo); + result.BeatsToHighlight = beat.BeatsToHighlight; return result; } @@ -273,7 +327,7 @@ private MasterBarTickLookup FindMasterBar(int tick) return bar; } - // search in lower half + // search in lower half if (tick < bar.Start) { top = middle - 1; @@ -289,7 +343,7 @@ private MasterBarTickLookup FindMasterBar(int tick) } /// - /// Gets the for a given masterbar at which the masterbar is played the first time. + /// Gets the for a given masterbar at which the masterbar is played the first time. /// /// The masterbar to find the time period for. /// A containing the details about the first time the is played. @@ -311,7 +365,7 @@ public MasterBarTickLookup GetMasterBar(MasterBar bar) } /// - /// Gets the start time in midi ticks for a given masterbar at which the masterbar is played the first time. + /// Gets the start time in midi ticks for a given masterbar at which the masterbar is played the first time. /// /// The masterbar to find the time period for. /// The time in midi ticks at which the masterbar is played the first time or 0 if the masterbar is not contained @@ -327,7 +381,7 @@ public int GetMasterBarStart(MasterBar bar) } /// - /// Adds a new to the lookup table. + /// Adds a new to the lookup table. /// /// The item to add. public void AddMasterBar(MasterBarTickLookup masterBar)