diff --git a/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs b/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs index b71059892..af5230016 100644 --- a/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs +++ b/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs @@ -99,7 +99,7 @@ public static async Task RunVisualTestScore(Score score, string referenceFileNam { Width = 1300 }; - renderer.PreRender.On(isResize => + renderer.PreRender.On(isResize => { result = new AlphaTab.Core.List(); totalWidth = 0.0; @@ -129,9 +129,7 @@ public static async Task RunVisualTestScore(Score score, string referenceFileNam }); renderer.Error.On((e) => { task.SetException(e); }); - var renderScore = - JsonConverter.JsObjectToScore(JsonConverter.ScoreToJsObject(score), settings); - renderer.RenderScore(renderScore, tracks); + renderer.RenderScore(score, tracks); if (await Task.WhenAny(task.Task, Task.Delay(2000)) == task.Task) { diff --git a/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadScoreRenderer.cs b/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadScoreRenderer.cs index 9c0e6efff..9ac669c5b 100644 --- a/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadScoreRenderer.cs +++ b/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadScoreRenderer.cs @@ -147,9 +147,8 @@ public void RenderScore(Score score, IList trackIndexes) } else { - var serialized = JsonConverter.ScoreToJsObject(score); _workerQueue.Add(() => - RenderScore(JsonConverter.JsObjectToScore(serialized, _renderer.Settings), + RenderScore(score, trackIndexes)); } } diff --git a/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt index 8ebb2fe56..173c0bab2 100644 --- a/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt +++ b/src.kotlin/alphaTab/src/jvmTest/kotlin/alphaTab/visualTests/VisualTestHelperPartials.kt @@ -159,9 +159,7 @@ class VisualTestHelperPartials { val job = GlobalScope.launch { try { - val renderScore = - JsonConverter.jsObjectToScore(JsonConverter.scoreToJsObject(score), settings); - renderer.renderScore(renderScore, actualTracks) + renderer.renderScore(score, actualTracks) } catch (e: Throwable) { error = e waitHandle.release() diff --git a/src/model/Beat.ts b/src/model/Beat.ts index f623b31d4..81e904ccb 100644 --- a/src/model/Beat.ts +++ b/src/model/Beat.ts @@ -802,8 +802,17 @@ export class Beat { this.graceGroup = new GraceGroup(); this.graceGroup.addBeat(this); this.graceGroup.isComplete = true; + this.graceGroup.finish(); this.updateDurations(); this.voice.insertBeat(this, cloneBeat); + + // ensure cloned beat has also a grace simple grace group for itself + // (see Voice.finish where every beat gets one) + // this ensures later that grace rods are assigned correctly to this beat. + cloneBeat.graceGroup = new GraceGroup(); + cloneBeat.graceGroup.addBeat(this); + cloneBeat.graceGroup.isComplete = true; + cloneBeat.graceGroup.finish(); } } diff --git a/src/model/GraceGroup.ts b/src/model/GraceGroup.ts index db6aae5d4..a7d4c0f92 100644 --- a/src/model/GraceGroup.ts +++ b/src/model/GraceGroup.ts @@ -16,7 +16,7 @@ export class GraceGroup { /** * true if the grace beat are followed by a normal beat within the same - * bar. + * bar. */ public isComplete: boolean = false; @@ -25,11 +25,14 @@ export class GraceGroup { * @param beat The beat to add */ public addBeat(beat: Beat) { - if (this.beats.length === 0) { - this.id = beat.absoluteDisplayStart + "_" + beat.voice.index; - } beat.graceIndex = this.beats.length; beat.graceGroup = this; this.beats.push(beat); } + + public finish() { + if (this.beats.length > 0) { + this.id = this.beats[0].absoluteDisplayStart + '_' + this.beats[0].voice.index; + } + } } diff --git a/src/model/Voice.ts b/src/model/Voice.ts index 0fe5bd0de..c6224a8cf 100644 --- a/src/model/Voice.ts +++ b/src/model/Voice.ts @@ -141,7 +141,8 @@ export class Voice { const lastGraceBeat = beat.graceGroup!.beats[beat.graceGroup!.beats.length - 1]; if (firstGraceBeat.graceType !== GraceType.BendGrace) { // find out the stolen duration first - let stolenDuration: number = (lastGraceBeat.playbackStart + lastGraceBeat.playbackDuration) - firstGraceBeat.playbackStart; + let stolenDuration: number = + lastGraceBeat.playbackStart + lastGraceBeat.playbackDuration - firstGraceBeat.playbackStart; switch (firstGraceBeat.graceType) { case GraceType.BeforeBeat: @@ -150,7 +151,8 @@ export class Voice { firstGraceBeat.previousBeat.playbackDuration -= stolenDuration; // place beats starting after new beat end if (firstGraceBeat.previousBeat.voice == this) { - currentPlaybackTick = firstGraceBeat.previousBeat.playbackStart + + currentPlaybackTick = + firstGraceBeat.previousBeat.playbackStart + firstGraceBeat.previousBeat.playbackDuration; } else { // stealing into the previous bar @@ -170,7 +172,7 @@ export class Voice { break; case GraceType.OnBeat: - // steal duration from current beat + // steal duration from current beat beat.playbackDuration -= stolenDuration; if (lastGraceBeat.voice === this) { // with changed durations, update current position to be after the last grace beat @@ -184,6 +186,9 @@ export class Voice { } } + beat.displayStart = currentDisplayTick; + beat.playbackStart = currentPlaybackTick; + if (beat.fermata) { this.bar.masterBar.addFermata(beat.playbackStart, beat.fermata); } else { @@ -191,11 +196,15 @@ export class Voice { } this._beatLookup.set(beat.playbackStart, beat); + } else { + beat.displayStart = currentDisplayTick; + beat.playbackStart = currentPlaybackTick; } - beat.displayStart = currentDisplayTick; - beat.playbackStart = currentPlaybackTick; beat.finishTuplet(); + if (beat.graceGroup) { + beat.graceGroup.finish(); + } currentDisplayTick += beat.displayDuration; currentPlaybackTick += beat.playbackDuration; }