diff --git a/src.csharp/AlphaTab/Core/TypeHelper.cs b/src.csharp/AlphaTab/Core/TypeHelper.cs index 161fd4814..b0894622d 100644 --- a/src.csharp/AlphaTab/Core/TypeHelper.cs +++ b/src.csharp/AlphaTab/Core/TypeHelper.cs @@ -376,6 +376,12 @@ public static string SubstringIndex(this string s, double startIndex, double end return s.Substring((int) startIndex, (int) (endIndex - startIndex)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Join(this IList s, string separator) + { + return string.Join(separator, s); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ReplaceAll(this string s, string before, string after) { diff --git a/src/model/Chord.ts b/src/model/Chord.ts index bb64efca6..914588276 100644 --- a/src/model/Chord.ts +++ b/src/model/Chord.ts @@ -1,7 +1,7 @@ import { Staff } from '@src/model/Staff'; -// TODO: rework model to specify for each finger -// on which frets they are placed. +// TODO: rework model to specify for each finger +// on which frets they are placed. /** * A chord definition. @@ -51,4 +51,19 @@ export class Chord { * Gets or sets whether the fingering is shown below the chord diagram. */ public showFingering: boolean = true; + /** + * Gets a unique id for this chord based on its properties. + */ + public get uniqueId(): string { + const properties = [ + this.name, + this.firstFret.toString(), + this.strings.join(','), + this.barreFrets.join(','), + this.showDiagram.toString(), + this.showFingering.toString(), + this.showName.toString() + ]; + return properties.join('|'); + } } diff --git a/src/rendering/layout/ScoreLayout.ts b/src/rendering/layout/ScoreLayout.ts index dade26e6a..3f35a1534 100644 --- a/src/rendering/layout/ScoreLayout.ts +++ b/src/rendering/layout/ScoreLayout.ts @@ -1,7 +1,6 @@ import { StaveProfile } from '@src/StaveProfile'; import { Environment } from '@src/Environment'; import { Bar } from '@src/model/Bar'; -import { Chord } from '@src/model/Chord'; import { Font, FontStyle, FontWeight } from '@src/model/Font'; import { Score } from '@src/model/Score'; import { Staff } from '@src/model/Staff'; @@ -209,15 +208,16 @@ export abstract class ScoreLayout { if (notation.isNotationElementVisible(NotationElement.ChordDiagrams)) { this.chordDiagrams = new ChordDiagramContainerGlyph(0, 0); this.chordDiagrams.renderer = fakeBarRenderer; - let chords: Map = new Map(); + let chordIds: Set = new Set(); + for (let track of this.renderer.tracks!) { for (let staff of track.staves) { const sc = staff.chords; if (sc) { - for (const [chordId, chord] of sc) { - if (!chords.has(chordId)) { + for (const [, chord] of sc) { + if (!chordIds.has(chord.uniqueId)) { if (chord.showDiagram) { - chords.set(chordId, chord); + chordIds.add(chord.uniqueId); this.chordDiagrams!.addChord(chord); } } diff --git a/test-data/visual-tests/effects-and-annotations/chords-duplicates.gp b/test-data/visual-tests/effects-and-annotations/chords-duplicates.gp new file mode 100644 index 000000000..f22e341f4 Binary files /dev/null and b/test-data/visual-tests/effects-and-annotations/chords-duplicates.gp differ diff --git a/test-data/visual-tests/effects-and-annotations/chords-duplicates.png b/test-data/visual-tests/effects-and-annotations/chords-duplicates.png new file mode 100644 index 000000000..e392a53ab Binary files /dev/null and b/test-data/visual-tests/effects-and-annotations/chords-duplicates.png differ diff --git a/test/visualTests/features/EffectsAndAnnotations.test.ts b/test/visualTests/features/EffectsAndAnnotations.test.ts index 59032338f..6b25d18fa 100644 --- a/test/visualTests/features/EffectsAndAnnotations.test.ts +++ b/test/visualTests/features/EffectsAndAnnotations.test.ts @@ -20,6 +20,11 @@ describe('EffectsAndAnnotationsTests', () => { await VisualTestHelper.runVisualTest('effects-and-annotations/chords.gp'); }); + it('chords-duplicates', async () => { + // This file was manually modified to contain 2 separate chords with the same details. + await VisualTestHelper.runVisualTest('effects-and-annotations/chords-duplicates.gp'); + }); + it('vibrato', async () => { await VisualTestHelper.runVisualTest('effects-and-annotations/vibrato.gp'); });