Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions Source/AlphaTab/AlphaTabApi.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using AlphaTab.Audio;
using AlphaTab.Audio.Generator;
using AlphaTab.Audio.Synth;
Expand Down Expand Up @@ -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);
}
}
}
Expand All @@ -926,7 +927,12 @@ private void CursorUpdateTick(int tick, bool stop = false)
/// <summary>
/// updates the cursors to highlight the specified beat
/// </summary>
private void CursorUpdateBeat(Beat beat, Beat nextBeat, double duration, bool stop)
private void CursorUpdateBeat(
Beat beat,
Beat nextBeat,
double duration,
bool stop,
FastList<Beat> beatsToHighlight = null)
{
if (beat == null)
{
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Source/AlphaTab/Audio/Generator/MidiFileGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
96 changes: 75 additions & 21 deletions Source/AlphaTab/Audio/MidiTickLookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ namespace AlphaTab.Audio
/// </summary>
public class BeatTickLookup
{
private FastDictionary<int, bool> _highlightedBeats;

/// <summary>
/// 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.
/// </summary>
public int Start { get; set; }

/// <summary>
/// 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.
/// </summary>
public int End { get; set; }

Expand All @@ -24,9 +26,30 @@ public class BeatTickLookup
public Beat Beat { get; set; }

/// <summary>
/// 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.
/// </summary>
public bool IsEmptyBar { get; set; }

/// <summary>
/// Gets or sets a list of all beats that should be highlighted when
/// the beat of this lookup starts playing.
/// </summary>
public FastList<Beat> BeatsToHighlight { get; set; }

public BeatTickLookup()
{
_highlightedBeats = new FastDictionary<int, bool>();
BeatsToHighlight = new FastList<Beat>();
}

public void HighlightBeat(Beat beat)
{
if (!_highlightedBeats.ContainsKey(beat.Id))
{
_highlightedBeats[beat.Id] = true;
BeatsToHighlight.Add(beat);
}
}
}

/// <summary>
Expand All @@ -35,12 +58,12 @@ public class BeatTickLookup
public class MasterBarTickLookup
{
/// <summary>
/// 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.
/// </summary>
public int Start { get; set; }

/// <summary>
/// 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.
/// </summary>
public int End { get; set; }

Expand All @@ -50,13 +73,13 @@ public class MasterBarTickLookup
public int Tempo { get; set; }

/// <summary>
/// Gets or sets the MasterBar which is played.
/// Gets or sets the MasterBar which is played.
/// </summary>
public MasterBar MasterBar { get; set; }

/// <summary>
/// Gets or sets the list of <see cref="BeatTickLookup"/> object which define the durations
/// for all <see cref="Beats"/> played within the period of this MasterBar.
/// for all <see cref="Beats"/> played within the period of this MasterBar.
/// </summary>
public FastList<BeatTickLookup> Beats { get; set; }

Expand All @@ -75,15 +98,15 @@ public MasterBarTickLookup()
}

/// <summary>
/// Performs the neccessary finalization steps after all information was written.
/// Performs the neccessary finalization steps after all information was written.
/// </summary>
public void Finish()
{
Beats.Sort((a, b) => a.Start - b.Start);
}

/// <summary>
/// Adds a new <see cref="BeatTickLookup"/> to the list of played beats during this MasterBar period.
/// Adds a new <see cref="BeatTickLookup"/> to the list of played beats during this MasterBar period.
/// </summary>
/// <param name="beat"></param>
public void AddBeat(BeatTickLookup beat)
Expand All @@ -93,29 +116,34 @@ public void AddBeat(BeatTickLookup beat)
}

/// <summary>
/// Represents the results of searching the currently played beat.
/// Represents the results of searching the currently played beat.
/// </summary>
/// <seealso cref="MidiTickLookup.FindBeat"/>
public class MidiTickLookupFindBeatResult
{
/// <summary>
/// Gets or sets the beat that is currently played.
/// Gets or sets the beat that is currently played.
/// </summary>
public Beat CurrentBeat { get; set; }

/// <summary>
/// Gets or sets the beat that will be played next.
/// Gets or sets the beat that will be played next.
/// </summary>
public Beat NextBeat { get; set; }

/// <summary>
/// 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.
/// </summary>
public int Duration { get; set; }

/// <summary>
/// Gets or sets the beats ot highlight along the current beat.
/// </summary>
public FastList<Beat> BeatsToHighlight { get; set; }
}

/// <summary>
/// This class holds all information about when <see cref="MasterBar"/>s and <see cref="Beat"/>s are played.
/// This class holds all information about when <see cref="MasterBar"/>s and <see cref="Beat"/>s are played.
/// </summary>
public class MidiTickLookup
{
Expand All @@ -130,7 +158,7 @@ public class MidiTickLookup
public FastDictionary<int, MasterBarTickLookup> MasterBarLookup { get; }

/// <summary>
/// Gets a list of all <see cref="MasterBarTickLookup"/> sorted by time.
/// Gets a list of all <see cref="MasterBarTickLookup"/> sorted by time.
/// </summary>
public FastList<MasterBarTickLookup> MasterBars { get; }

Expand All @@ -144,11 +172,12 @@ public MidiTickLookup()
}

/// <summary>
/// Performs the neccessary finalization steps after all information was written.
/// Performs the neccessary finalization steps after all information was written.
/// </summary>
public void Finish()
{
MasterBarTickLookup previous = null;
var activeBeats = new FastList<BeatTickLookup>();
foreach (var bar in MasterBars)
{
bar.Finish();
Expand All @@ -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<BeatTickLookup>();
// 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;
}
}

/// <summary>
/// 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.
/// </summary>
/// <param name="tracks">The tracks in which to search the played beat for.</param>
/// <param name="tick">The current time in midi ticks.</param>
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand All @@ -289,7 +343,7 @@ private MasterBarTickLookup FindMasterBar(int tick)
}

/// <summary>
/// Gets the <see cref="MasterBarTickLookup"/> for a given masterbar at which the masterbar is played the first time.
/// Gets the <see cref="MasterBarTickLookup"/> for a given masterbar at which the masterbar is played the first time.
/// </summary>
/// <param name="bar">The masterbar to find the time period for. </param>
/// <returns>A <see cref="MasterBarTickLookup"/> containing the details about the first time the <see cref="MasterBar"/> is played.</returns>
Expand All @@ -311,7 +365,7 @@ public MasterBarTickLookup GetMasterBar(MasterBar bar)
}

/// <summary>
/// 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.
/// </summary>
/// <param name="bar">The masterbar to find the time period for. </param>
/// <returns>The time in midi ticks at which the masterbar is played the first time or 0 if the masterbar is not contained</returns>
Expand All @@ -327,7 +381,7 @@ public int GetMasterBarStart(MasterBar bar)
}

/// <summary>
/// Adds a new <see cref="MasterBarTickLookup"/> to the lookup table.
/// Adds a new <see cref="MasterBarTickLookup"/> to the lookup table.
/// </summary>
/// <param name="masterBar">The item to add. </param>
public void AddMasterBar(MasterBarTickLookup masterBar)
Expand Down