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
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,7 @@ export class BrowserUiFacade implements IUiFacade<unknown> {
beatCursor.style.willChange = 'transform';
beatCursorContainer.width = 3;
beatCursorContainer.height = 1;
beatCursorContainer.centerAtPosition = true;
beatCursorContainer.setBounds(0, 0, 1, 1);

// add cursors to UI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export class ScalableHtmlElementContainer extends HtmlElementContainer {
private _xscale: number;
private _yscale: number;

public centerAtPosition = false;

public constructor(element: HTMLElement, xscale: number, yscale: number) {
super(element);
this._xscale = xscale;
Expand Down Expand Up @@ -63,7 +65,11 @@ export class ScalableHtmlElementContainer extends HtmlElementContainer {
h = h / this._yscale;
}

this.element.style.transform = `translate(${x}px, ${y}px) scale(${w}, ${h})`;
let transform = `translate(${x}px, ${y}px) scale(${w}, ${h})`;
if(this.centerAtPosition) {
transform += ` translateX(-50%)`;
}
this.element.style.transform = transform;
this.element.style.transformOrigin = 'top left';
this.lastBounds.x = x;
this.lastBounds.y = y;
Expand Down
2 changes: 1 addition & 1 deletion packages/alphatab/src/rendering/BarRendererBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ export class BarRendererBase {
case BeatXPosition.OnNotes:
return container.voiceContainer.x + container.x + container.onNotes.x;
case BeatXPosition.MiddleNotes:
return container.voiceContainer.x + container.x + container.onTimeX;
return container.voiceContainer.x + container.x + container.onNotes.x + container.onNotes.middleX;
case BeatXPosition.Stem:
const offset = container.onNotes.beamingHelper
? container.onNotes.beamingHelper.getBeatLineX(beat)
Expand Down
2 changes: 1 addition & 1 deletion packages/alphatab/src/rendering/BeatXPosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export enum BeatXPosition {
*/
OnNotes = 1,
/**
* Gets the middle-notes position which is located after in the middle the note heads.
* Gets the middle-notes position which is located after in the exact center of the note heads.
*/
MiddleNotes = 2,
/**
Expand Down
10 changes: 5 additions & 5 deletions packages/alphatab/src/rendering/glyphs/BeatContainerGlyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class BeatContainerGlyph extends Glyph {
public minWidth: number = 0;

public get onTimeX(): number {
return this.onNotes.x + this.onNotes.centerX;
return this.onNotes.x + this.onNotes.onTimeX;
}

public constructor(beat: Beat, voiceContainer: VoiceContainerGlyph) {
Expand Down Expand Up @@ -59,9 +59,9 @@ export class BeatContainerGlyph extends Glyph {
}

public registerLayoutingInfo(layoutings: BarLayoutingInfo): void {
const preBeatStretch: number = this.preNotes.computedWidth + this.onNotes.centerX;
const preBeatStretch: number = this.preNotes.computedWidth + this.onNotes.onTimeX;

let postBeatStretch: number = this.onNotes.computedWidth - this.onNotes.centerX;
let postBeatStretch: number = this.onNotes.computedWidth - this.onNotes.onTimeX;
// make space for flag
const helper = this.renderer.helpers.getBeamingHelperForBeat(this.beat);
if (this.beat.graceType !== GraceType.None) {
Expand Down Expand Up @@ -229,7 +229,7 @@ export class BeatContainerGlyph extends Glyph {
beatBoundings.realBounds.w = this.width;
beatBoundings.realBounds.h = barBounds.realBounds.h;

beatBoundings.onNotesX = cx + this.x + this.onNotes.centerX;
beatBoundings.onNotesX = cx + this.x + this.onNotes.x + this.onNotes.onTimeX;
} else {
beatBoundings.visualBounds = new Bounds();
beatBoundings.visualBounds.x = cx + this.x;
Expand Down Expand Up @@ -269,7 +269,7 @@ export class BeatContainerGlyph extends Glyph {
beatBoundings.realBounds.w = this.width;
beatBoundings.realBounds.h = barBounds.realBounds.h;

beatBoundings.onNotesX = cx + this.x + this.onNotes.x + this.onNotes.centerX;
beatBoundings.onNotesX = cx + this.x + this.onNotes.x + this.onNotes.onTimeX;
}

barBounds.addBeat(beatBoundings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import type { BeatBounds } from '@coderline/alphatab/rendering/utils/BeatBounds'
*/
export class BeatOnNoteGlyphBase extends BeatGlyphBase {
public beamingHelper!: BeamingHelper;
public centerX: number = 0;
public onTimeX: number = 0;
public middleX: number = 0;

public updateBeamingHelper(): void {
//
Expand Down
7 changes: 4 additions & 3 deletions packages/alphatab/src/rendering/glyphs/NumberedBeatGlyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,11 +357,12 @@ export class NumberedBeatGlyph extends BeatOnNoteGlyphBase {
super.doLayout();

if (this.container.beat.isEmpty) {
this.centerX = this.width / 2;
this.onTimeX = this.width / 2;
} else if (this.noteHeads) {
this.centerX = this.noteHeads.x + this.noteHeads.width / 2;
this.onTimeX = this.noteHeads.x + this.noteHeads.width / 2;
} else if (this.deadSlapped) {
this.centerX = this.deadSlapped.x + this.deadSlapped.width / 2;
this.onTimeX = this.deadSlapped.x + this.deadSlapped.width / 2;
}
this.middleX = this.onTimeX;
}
}
9 changes: 6 additions & 3 deletions packages/alphatab/src/rendering/glyphs/ScoreBeatGlyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,14 @@ export class ScoreBeatGlyph extends BeatOnNoteGlyphBase {
}
super.doLayout();
if (this.container.beat.isEmpty) {
this.centerX = this.width / 2;
this.onTimeX = this.width / 2;
this.middleX = this.onTimeX;
} else if (this.restGlyph) {
this.centerX = this.restGlyph!.x + this.restGlyph!.width / 2;
this.onTimeX = this.restGlyph!.x + this.restGlyph!.width / 2;
this.middleX = this.onTimeX;
} else if (this.noteHeads) {
this.centerX = this.noteHeads!.x + this.noteHeads!.width / 2;
this.onTimeX = this.noteHeads!.x + this.noteHeads!.onTimeX;
this.middleX = this.noteHeads!.x + this.noteHeads!.width / 2;
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/alphatab/src/rendering/glyphs/ScoreNoteChordGlyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ export class ScoreNoteChordGlyph extends ScoreNoteChordGlyphBase {
this._deadSlapped.renderer = this.renderer;
this._deadSlapped.doLayout();
this.width = this._deadSlapped.width;
this.onTimeX = this.width / 2;

}

let aboveBeatEffectsY = 0;
Expand Down
30 changes: 28 additions & 2 deletions packages/alphatab/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export abstract class ScoreNoteChordGlyphBase extends Glyph {
public downLineX: number = 0;
public noteStartX: number = 0;

public onTimeX = 0;

public constructor() {
super(0, 0);
}
Expand Down Expand Up @@ -120,14 +122,15 @@ export abstract class ScoreNoteChordGlyphBase extends Glyph {
const stemPosition = anyDisplaced || direction === BeamDirection.Up ? stemUpX : stemDownX;

let w: number = 0;
let displacedWidth = 0;
let nonDisplacedWidth = 0;
for (let i: number = 0, j: number = this._infos.length; i < j; i++) {
const g = this._infos[i].glyph;
const alignDisplaced: boolean = displaced.get(i)!;

if (alignDisplaced) {
// displaced: shift note to stem position
g.x = stemPosition;
// TODO: shift left?
} else {
// not displaced: align on left side (where down stem would be for notes)
g.x = stemDownX;
Expand All @@ -137,7 +140,13 @@ export abstract class ScoreNoteChordGlyphBase extends Glyph {
}

g.x += this.noteStartX;
w = Math.max(w, g.x + g.width);
const gw = g.x + g.width;
w = Math.max(w, gw);
if (alignDisplaced) {
displacedWidth = Math.max(displacedWidth, gw);
} else {
nonDisplacedWidth = Math.max(nonDisplacedWidth, gw);
}

// after size calculation, re-align glyph to stem if needed
if (g instanceof NoteHeadGlyph && (g as NoteHeadGlyph).centerOnStem) {
Expand All @@ -152,6 +161,23 @@ export abstract class ScoreNoteChordGlyphBase extends Glyph {
this.upLineX = stemUpX;
this.downLineX = stemDownX;
}

// the center of score notes, (used for aligning the beat to the right on-time position)
// is always the center of the "correct note" position.
// * If the stem is upwards, the center is the middle of the left hand side note head
// * If the stem is downards, the center is the middle of the right-hand-side note head
if (anyDisplaced) {
if (direction === BeamDirection.Up) {
this.onTimeX = nonDisplacedWidth / 2;
} else {
const displacedRawWith = displacedWidth - stemPosition;
this.onTimeX = stemPosition + (displacedRawWith / 2);
}
} else {
// for no displaced notes it is simply the center
this.onTimeX = w / 2;
}

this.width = w;
}

Expand Down
9 changes: 5 additions & 4 deletions packages/alphatab/src/rendering/glyphs/SlashBeatGlyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,14 @@ export class SlashBeatGlyph extends BeatOnNoteGlyphBase {
super.doLayout();

if (this.container.beat.isEmpty) {
this.centerX = this.width / 2;
this.onTimeX = this.width / 2;
} else if (this.restGlyph) {
this.centerX = this.restGlyph.x + this.restGlyph.width / 2;
this.onTimeX = this.restGlyph.x + this.restGlyph.width / 2;
} else if (this.noteHeads) {
this.centerX = this.noteHeads.x + this.noteHeads.width / 2;
this.onTimeX = this.noteHeads.x + this.noteHeads.width / 2;
} else if (this.deadSlapped) {
this.centerX = this.deadSlapped.x + this.deadSlapped.width / 2;
this.onTimeX = this.deadSlapped.x + this.deadSlapped.width / 2;
}
this.middleX = this.onTimeX;
}
}
11 changes: 6 additions & 5 deletions packages/alphatab/src/rendering/glyphs/TabBeatGlyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,18 @@ export class TabBeatGlyph extends BeatOnNoteGlyphBase {
this.width = w;
this.computedWidth = w;
if (this.container.beat.isEmpty) {
this.centerX = this.width / 2;
this.onTimeX = this.width / 2;
} else if (this.restGlyph) {
this.centerX = this.restGlyph!.x + this.restGlyph!.width / 2;
this.onTimeX = this.restGlyph!.x + this.restGlyph!.width / 2;
} else if (this.noteNumbers) {
this.centerX = this.noteNumbers!.x + this.noteNumbers!.noteStringWidth / 2;
this.onTimeX = this.noteNumbers!.x + this.noteNumbers!.noteStringWidth / 2;
} else if (this.slash) {
this.centerX = this.slash!.x + this.slash!.width / 2;
this.onTimeX = this.slash!.x + this.slash!.width / 2;
}
this.middleX = this.onTimeX;

for (const g of centeredEffectGlyphs) {
g.x = this.centerX;
g.x = this.onTimeX;
}
}

Expand Down
Binary file modified packages/alphatab/test-data/musicxml-samples/BeetAnGeSample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/alphatab/test-data/musicxml-samples/DebuMandSample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/alphatab/test-data/musicxml-samples/FaurReveSample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/alphatab/test-data/musicxml-samples/MozaChloSample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/alphatab/test-data/musicxml-samples/MozartTrio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/alphatab/test-data/musicxml-samples/SchbAvMaSample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/playground/test-results.html
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
</head>

<body>
<h1>alphaTab - Visual Test Results</h1>
<h1>alphaTab - Visual Test Results <span id="remaining"></span></h1>
<p>
This page contains any failing visual tests for comparison and acceptance.
Run the <code>visualTests</code> via <code>npm run test</code> or using <a
Expand Down
15 changes: 15 additions & 0 deletions packages/playground/test-results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ type TestResult = {
originalFile: string;
newFile: string | Uint8Array;
diffFile: string | Uint8Array;
accepted?: true;
};

let currentResults:TestResult[] = [];

function setupComparer(card: HTMLElement, el: HTMLElement, result: TestResult) {
const ex = el.querySelector<HTMLElement>('.expected')!;
const ac = el.querySelector<HTMLElement>('.actual')!;
Expand Down Expand Up @@ -64,6 +67,8 @@ function setupComparer(card: HTMLElement, el: HTMLElement, result: TestResult) {
acceptButton.innerText = 'Accepted';
}
card.classList.add('accepted');
result.accepted = true;
updateRemaining();
};
xhr.onerror = () => {
alert('error accepting test result');
Expand Down Expand Up @@ -171,6 +176,7 @@ async function createResultViewer(result: TestResult) {
async function displayResults(results: TestResult[]) {
const wrapper = document.querySelector<HTMLElement>('#results-wrapper')!;
wrapper.innerHTML = '';
currentResults = results;

for (const result of results) {
wrapper.appendChild(await createResultViewer(result));
Expand All @@ -179,6 +185,15 @@ async function displayResults(results: TestResult[]) {
if (results.length === 0) {
wrapper.innerHTML = '<div class="alert alert-success" role="alert">No reported errors on visual tests.</div>';
}
updateRemaining();
}

function updateRemaining() {
if (currentResults.length === 0) {
return;
}
document.querySelector<HTMLSpanElement>('#remaining')!.innerText =
`(${currentResults.filter(r => !r.accepted).length}/${currentResults.length})`;
}

function loadResults() {
Expand Down