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
1 change: 1 addition & 0 deletions packages/alphatab/src/DisplaySettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ export class DisplaySettings {
*
* * Comparing files against each other (top/bottom comparison)
* * Aligning the playback of multiple files on one screen assuming the same tempo (e.g. one file per track).
* @deprecated Use the {@link LayoutMode.Parchment} to display a music sheet respecting the systems layout.
*/
public systemsLayoutMode: SystemsLayoutMode = SystemsLayoutMode.Automatic;
}
7 changes: 7 additions & 0 deletions packages/alphatab/src/Environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import { WideBeatVibratoEffectInfo } from '@coderline/alphatab/rendering/effects
import { WideNoteVibratoEffectInfo } from '@coderline/alphatab/rendering/effects/WideNoteVibratoEffectInfo';
import { HorizontalScreenLayout } from '@coderline/alphatab/rendering/layout/HorizontalScreenLayout';
import { PageViewLayout } from '@coderline/alphatab/rendering/layout/PageViewLayout';
import { ParchmentLayout } from '@coderline/alphatab/rendering/layout/ParchmentLayout';
import type { ScoreLayout } from '@coderline/alphatab/rendering/layout/ScoreLayout';
import { NumberedBarRenderer } from '@coderline/alphatab/rendering/NumberedBarRenderer';
import { NumberedBarRendererFactory } from '@coderline/alphatab/rendering/NumberedBarRendererFactory';
Expand Down Expand Up @@ -623,6 +624,12 @@ export class Environment {
return new HorizontalScreenLayout(r);
})
);
engines.set(
LayoutMode.Parchment,
new LayoutEngineFactory(true, r => {
return new ParchmentLayout(r);
})
);
return engines;
}

Expand Down
39 changes: 38 additions & 1 deletion packages/alphatab/src/LayoutMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,43 @@ export enum LayoutMode {
Page = 0,
/**
* Bars are aligned horizontally in [one horizontally endless system (row)](https://alphatab.net/docs/showcase/layouts#horizontal-layout)
*
* alphaTab holds following information in the data model and developers can change those values (e.g. by tapping into the `scoreLoaded`) event.
* These widths are respected when using this layout.
*
* **Used when single tracks are rendered:**
*
* * `score.tracks[index].staves[index].bars[index].displayWidth` - The absolute size of this bar when displayed.
*
* **Used when multiple tracks are rendered:**
*
* * `score.masterBars[index].displayWidth` - Like the `displayWidth` on bar level.
*/
Horizontal = 1
Horizontal = 1,
/**
* The bars are aligned in an [vertically endless page-style fashion](https://alphatab.net/docs/showcase/layouts#parchment)
* respecting the configured systems layout.
*
* The parchment layout uses the `systemsLayout` and `defaultSystemsLayout` to decide how many bars go into a single system (row).
* Additionally when sizing the bars within the system the `displayScale` is used. This scale is rather a ratio than an absolute percentage value but percentages work also:
*
* ![Parchment Layout](https://alphatab.net/img/reference/property/systems-layout-page-examples.png)
*
* File formats like Guitar Pro embed information about the layout in the file and alphaTab can read and use this information.
*
* alphaTab holds following information in the data model and developers can change those values (e.g. by tapping into the `scoreLoaded`) event.
*
* **Used when single tracks are rendered:**
*
* * `score.tracks[index].systemsLayout` - An array of numbers describing how many bars should be placed within each system (row).
* * `score.tracks[index].defaultSystemsLayout` - The number of bars to place in a system (row) when no value is defined in the `systemsLayout`.
* * `score.tracks[index].staves[index].bars[index].displayScale` - The relative size of this bar in the system it is placed. Note that this is not directly a percentage value. e.g. if there are 3 bars and all define scale 1, they are sized evenly.
*
* **Used when multiple tracks are rendered:**
*
* * `score.systemsLayout` - Like the `systemsLayout` on track level.
* * `score.defaultSystemsLayout` - Like the `defaultSystemsLayout` on track level.
* * `score.masterBars[index].displayScale` - Like the `displayScale` on bar level.
*/
Parchment = 2
}
3 changes: 0 additions & 3 deletions packages/alphatab/src/model/Chord.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type { Staff } from '@coderline/alphatab/model/Staff';

// TODO: rework model to specify for each finger
// on which frets they are placed.

/**
* A chord definition.
* @json
Expand Down
2 changes: 0 additions & 2 deletions packages/alphatab/src/model/ElementStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,4 @@ export class ElementStyle<TSubElements extends number> {
* even if some "higher level" element changes colors.
*/
public colors: Map<TSubElements, Color | null> = new Map<TSubElements, Color | null>();

// TODO: replace NotationSettings.elements by adding a visibility here?
}
34 changes: 27 additions & 7 deletions packages/alphatab/src/model/ModelUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AccidentalType } from '@coderline/alphatab/model/AccidentalType';
import { Automation, AutomationType } from '@coderline/alphatab/model/Automation';
import { Bar } from '@coderline/alphatab/model/Bar';
import { Beat } from '@coderline/alphatab/model/Beat';
import type { Duration } from '@coderline/alphatab/model/Duration';
import { Duration } from '@coderline/alphatab/model/Duration';
import type { KeySignature } from '@coderline/alphatab/model/KeySignature';
import { MasterBar } from '@coderline/alphatab/model/MasterBar';
import { NoteAccidentalMode } from '@coderline/alphatab/model/NoteAccidentalMode';
Expand Down Expand Up @@ -43,13 +43,18 @@ export class TuningParseResultTone {
* @internal
*/
export class ModelUtils {
private static readonly _durationIndices = ModelUtils._buildDurationIndices();

private static _buildDurationIndices() {
return new Map<Duration, number>(
Object.values(Duration)
.filter<any>((k: any) => typeof k === 'number')
.map(d => [d as number as Duration, (d as number) < 0 ? 0 : Math.log2(d as number) | 0])
);
}

public static getIndex(duration: Duration): number {
const index: number = 0;
const value: number = duration;
if (value < 0) {
return index;
}
return Math.log2(duration) | 0;
return ModelUtils._durationIndices.get(duration)!;
}

public static keySignatureIsFlat(ks: number): boolean {
Expand Down Expand Up @@ -944,4 +949,19 @@ export class ModelUtils {
}
return a > b ? a : b;
}

public static getSystemLayout(score: Score, systemIndex: number, displayedTracks: Track[]) {
let defaultSystemsLayout: number;
let systemsLayout: number[];
if (displayedTracks.length === 1) {
defaultSystemsLayout = displayedTracks[0].defaultSystemsLayout;
systemsLayout = displayedTracks[0].systemsLayout;
} else {
// multi track applies
defaultSystemsLayout = score.defaultSystemsLayout;
systemsLayout = score.systemsLayout;
}

return systemIndex < systemsLayout.length ? systemsLayout[systemIndex] : defaultSystemsLayout;
}
}
5 changes: 3 additions & 2 deletions packages/alphatab/src/model/Score.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,10 @@ export class Score {
if (this.masterBars.length !== 0) {
bar.previousMasterBar = this.masterBars[this.masterBars.length - 1];
bar.previousMasterBar.nextMasterBar = bar;
// TODO: this will not work on anacrusis. Correct anacrusis durations are only working
// NOTE: this will not work on anacrusis. Correct anacrusis durations are only working
// when there are beats with playback positions already computed which requires full finish
// chicken-egg problem here. temporarily forcing anacrusis length here to 0
// chicken-egg problem here. temporarily forcing anacrusis length here to 0,
// .finish() will correct these times
bar.start =
bar.previousMasterBar.start +
(bar.previousMasterBar.isAnacrusis ? 0 : bar.previousMasterBar.calculateDuration());
Expand Down
31 changes: 1 addition & 30 deletions packages/alphatab/src/rendering/BarRendererBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { Note } from '@coderline/alphatab/model/Note';
import { SimileMark } from '@coderline/alphatab/model/SimileMark';
import { type Voice, VoiceSubElement } from '@coderline/alphatab/model/Voice';
import { CanvasHelper, type ICanvas } from '@coderline/alphatab/platform/ICanvas';
import type { RenderingResources } from '@coderline/alphatab/RenderingResources';
import { BeatXPosition } from '@coderline/alphatab/rendering/BeatXPosition';
import { EffectBandContainer } from '@coderline/alphatab/rendering/EffectBandContainer';
import {
Expand All @@ -15,7 +16,6 @@ import type { Glyph } from '@coderline/alphatab/rendering/glyphs/Glyph';
import { LeftToRightLayoutingGlyphGroup } from '@coderline/alphatab/rendering/glyphs/LeftToRightLayoutingGlyphGroup';
import { MultiVoiceContainerGlyph } from '@coderline/alphatab/rendering/glyphs/MultiVoiceContainerGlyph';
import { ContinuationTieGlyph, type ITieGlyph, type TieGlyph } from '@coderline/alphatab/rendering/glyphs/TieGlyph';
import { InternalSystemsLayoutMode } from '@coderline/alphatab/rendering/layout/ScoreLayout';
import { MultiBarRestBeatContainerGlyph } from '@coderline/alphatab/rendering/MultiBarRestBeatContainerGlyph';
import type { ScoreRenderer } from '@coderline/alphatab/rendering/ScoreRenderer';
import type { BarLayoutingInfo } from '@coderline/alphatab/rendering/staves/BarLayoutingInfo';
Expand All @@ -27,7 +27,6 @@ import type { BeamingHelper } from '@coderline/alphatab/rendering/utils/BeamingH
import { Bounds } from '@coderline/alphatab/rendering/utils/Bounds';
import { ElementStyleHelper } from '@coderline/alphatab/rendering/utils/ElementStyleHelper';
import type { MasterBarBounds } from '@coderline/alphatab/rendering/utils/MasterBarBounds';
import type { RenderingResources } from '@coderline/alphatab/RenderingResources';
import type { Settings } from '@coderline/alphatab/Settings';

/**
Expand Down Expand Up @@ -246,22 +245,6 @@ export class BarRendererBase {
return this.scoreRenderer.settings;
}

/**
* Gets the scale with which the bar should be displayed in case the model
* scale should be respected.
*/
public get barDisplayScale(): number {
return this.staff!.system.staves.length > 1 ? this.bar.masterBar.displayScale : this.bar.displayScale;
}

/**
* Gets the absolute width in which the bar should be displayed in case the model
* scale should be respected.
*/
public get barDisplayWidth(): number {
return this.staff!.system.staves.length > 1 ? this.bar.masterBar.displayWidth : this.bar.displayWidth;
}

protected wasFirstOfStaff: boolean = false;

public get isFirstOfStaff(): boolean {
Expand Down Expand Up @@ -329,18 +312,6 @@ export class BarRendererBase {
this.width = Math.ceil(this._postBeatGlyphs.x + this._postBeatGlyphs.width);
this.computedWidth = this.width;

// For cases like in the horizontal layout we need to set the fixed width early
// to have correct partials splitting. the proper alignment to this scale will happen
// later in the workflow.
const fixedBarWidth = this.barDisplayWidth;
if (
fixedBarWidth > 0 &&
this.scoreRenderer.layout!.systemsLayoutMode === InternalSystemsLayoutMode.FromModelWithWidths
) {
this.width = fixedBarWidth;
this.computedWidth = fixedBarWidth;
}

this.topEffects.sizeAndAlignEffectBands();
this.bottomEffects.sizeAndAlignEffectBands();
this._registerStaffOverflow();
Expand Down
2 changes: 0 additions & 2 deletions packages/alphatab/src/rendering/ScoreBeatContainerGlyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,6 @@ export class ScoreBeatContainerGlyph extends BeatContainerGlyph {
const tie: ScoreTieGlyph = new ScoreTieGlyph(`score.tie.${n.tieOrigin!.id}`, n.tieOrigin!, n, true);
this.addTie(tie);
}
// TODO: depending on the type we have other positioning
// we should place glyphs in the preNotesGlyph or postNotesGlyph if needed
if (n.slideInType !== SlideInType.None || n.slideOutType !== SlideOutType.None) {
const l: ScoreSlideLineGlyph = new ScoreSlideLineGlyph(n.slideInType, n.slideOutType, n, this);
this.addTie(l);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export class BendNoteHeadGroupGlyph extends ScoreNoteChordGlyphBase {
}

protected override getScoreChordNoteHeadInfo(): ScoreChordNoteHeadInfo {
// TODO: do we need to share this spacing across all staves&tracks?
const staff = this._beat.voice.bar.staff;
const key = `score.noteheads.${this._groupId}.${staff.track.index}.${staff.index}.${this._beat.absoluteDisplayStart}`;
let existing = this.renderer.staff!.getSharedLayoutData<ScoreChordNoteHeadInfo | undefined>(key, undefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export class ScoreNoteChordGlyph extends ScoreNoteChordGlyphBase {
return new ScoreChordNoteHeadInfo(this.direction);
}

// TODO: do we need to share this spacing across all staves&tracks?
const staff = this.beat.voice.bar.staff;
const key = `score.noteheads.${staff.track.index}.${staff.index}.${this.beat.absoluteDisplayStart}`;
let existing = this.renderer.staff!.getSharedLayoutData<ScoreChordNoteHeadInfo | undefined>(key, undefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import type { ScoreBarRenderer } from '@coderline/alphatab/rendering/ScoreBarRen
import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection';
import { ElementStyleHelper } from '@coderline/alphatab/rendering/utils/ElementStyleHelper';

// TODO[perf]: the overall note head alignment creates quite a lot of objects which the GC
// will have to cleanup again. we should be optimize this (e.g. via object pooling?, checking for multi-voice and avoid some objects)

/**
* @internal
* @record
Expand Down Expand Up @@ -233,7 +230,6 @@ export class ScoreChordNoteHeadInfo {
}

private static _canShareNoteHead(mainGroup: ScoreChordNoteHeadGroup, thisGroup: ScoreChordNoteHeadGroup) {
// TODO: check actual note head
const mainGroupBottom = mainGroup.direction === BeamDirection.Up ? mainGroup.maxStep : mainGroup.minStep;
const thisGroupBottom = thisGroup.direction === BeamDirection.Up ? thisGroup.maxStep : thisGroup.minStep;

Expand Down Expand Up @@ -308,8 +304,6 @@ interface ScoreNoteGlyphInfo {
*/
export abstract class ScoreNoteChordGlyphBase extends Glyph {
private _infos: ScoreNoteGlyphInfo[] = [];
// TODO[perf]: keeping the whole group only for stemX prevents the GC to collect this
// maybe we can do some better "finalization" of the groups once all voices have been done
private _noteHeadInfo?: ScoreChordNoteHeadInfo;
protected noteGroup?: ScoreChordNoteHeadGroup;

Expand Down
Loading