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
2 changes: 1 addition & 1 deletion packages/alphatab/scripts/JsonDeclarationEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as ts from 'typescript';
import createEmitter, { generateFile } from './EmitterBase';
import { getTypeWithNullableInfo, type TypeWithNullableInfo } from './TypeSchema';

function createDefaultJsonTypeNode(checker: ts.TypeChecker, type: TypeWithNullableInfo, isNullable: boolean) {
function createDefaultJsonTypeNode(checker: ts.TypeChecker, type: TypeWithNullableInfo, isNullable: boolean): ts.TypeNode {
if (isNullable) {
const notNullable = createDefaultJsonTypeNode(checker, type, false);
return ts.factory.createUnionTypeNode([notNullable, ts.factory.createLiteralTypeNode(ts.factory.createNull())]);
Expand Down
23 changes: 12 additions & 11 deletions packages/alphatab/scripts/smufl-metadata.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import fs from 'node:fs';
import url from 'node:url';
import path from 'node:path';
import { MusicFontSymbol } from '../src/model/MusicFontSymbol';
import { SmuflMetrics } from '../src/SmuflMetrics';
import url from 'node:url';
import { EngravingSettings } from '@coderline/alphatab/EngravingSettings';
import { MusicFontSymbol } from '@coderline/alphatab/model/MusicFontSymbol';
import { SmuflMetadata } from '@coderline/alphatab/SmuflMetadata';

const input = process.argv[2];
const output = process.argv[3];

const metadata = JSON.parse(await fs.promises.readFile(input, 'utf-8'));
const metadata: SmuflMetadata = JSON.parse(await fs.promises.readFile(input, 'utf-8'));

const outputMetadata = {
const outputMetadata:SmuflMetadata = {
engravingDefaults: metadata.engravingDefaults,
glyphBBoxes: {},
glyphsWithAnchors: {}
Expand All @@ -20,20 +21,20 @@ for(const [_,name] of Object.entries(MusicFontSymbol).filter(e => typeof e[1] ==
alphaTabUsedGlyphs.add(name.toString().toLowerCase());
}

for(const [k,_] of SmuflMetrics.smuflNameToGlyphNameMapping) {
for(const [k,_] of EngravingSettings.smuflNameToGlyphNameMapping) {
alphaTabUsedGlyphs.add(k.toLowerCase());
}

for(const name of Object.keys(metadata.glyphBBoxes)) {
for(const name of Object.keys(metadata.glyphBBoxes!)) {
if(alphaTabUsedGlyphs.has(name.toLowerCase())) {
const alphaTabName = SmuflMetrics.smuflNameToGlyphNameMapping.has(name) ? SmuflMetrics.smuflNameToGlyphNameMapping.get(name)! : name;
outputMetadata.glyphBBoxes[alphaTabName] = metadata.glyphBBoxes[name];
const alphaTabName = EngravingSettings.smuflNameToGlyphNameMapping.has(name) ? EngravingSettings.smuflNameToGlyphNameMapping.get(name)! : name;
outputMetadata.glyphBBoxes![alphaTabName] = metadata.glyphBBoxes![name];
}
}

for(const name of Object.keys(metadata.glyphsWithAnchors)) {
if(alphaTabUsedGlyphs.has(name.toLowerCase())) {
const alphaTabName = SmuflMetrics.smuflNameToGlyphNameMapping.has(name) ? SmuflMetrics.smuflNameToGlyphNameMapping.get(name)! : name;
const alphaTabName = EngravingSettings.smuflNameToGlyphNameMapping.has(name) ? EngravingSettings.smuflNameToGlyphNameMapping.get(name)! : name;
outputMetadata.glyphsWithAnchors[alphaTabName] = metadata.glyphsWithAnchors[name];
}
}
Expand All @@ -44,7 +45,7 @@ await fs.promises.writeFile(output,

if(process.argv[4] === 'true') {
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
const smuflMetricsFile = path.resolve(__dirname, '..', 'src', 'SmuflMetrics.ts');
const smuflMetricsFile = path.resolve(__dirname, '..', 'src', 'EngravingSettings.ts');
let source = await fs.promises.readFile(smuflMetricsFile, 'utf-8');
const beginMarker = 'begin bravura_alphatab_metadata';
const endMarker = 'end bravura_alphatab_metadata';
Expand Down
35 changes: 33 additions & 2 deletions packages/alphatab/src/EngravingSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,22 @@ export class EngravingSettings {
this.stemFlagOffsets.set(Duration.OneHundredTwentyEighth, 0);
this.stemFlagOffsets.set(Duration.TwoHundredFiftySixth, 0);

// Workaround for: https://github.com/w3c/smufl/issues/203
// There is no clear anchor for the height of flags on the stem side yet.
// These aproximations are tested with bravura

this.stemFlagHeight.set(Duration.QuadrupleWhole, 0);
this.stemFlagHeight.set(Duration.DoubleWhole, 0);
this.stemFlagHeight.set(Duration.Whole, 0);
this.stemFlagHeight.set(Duration.Half, 0);
this.stemFlagHeight.set(Duration.Quarter, 0);
this.stemFlagHeight.set(Duration.Eighth, 1 * this.oneStaffSpace);
this.stemFlagHeight.set(Duration.Sixteenth, 1.5 * this.oneStaffSpace);
this.stemFlagHeight.set(Duration.ThirtySecond, 2 * this.oneStaffSpace);
this.stemFlagHeight.set(Duration.SixtyFourth, 3 * this.oneStaffSpace);
this.stemFlagHeight.set(Duration.OneHundredTwentyEighth, 3.5 * this.oneStaffSpace);
this.stemFlagHeight.set(Duration.TwoHundredFiftySixth, 4.2 * this.oneStaffSpace);

for (const [g, v] of Object.entries(smufl.glyphsWithAnchors)) {
const symbol = EngravingSettings._smuflNameToMusicFontSymbol(g);
if (symbol) {
Expand Down Expand Up @@ -759,11 +775,26 @@ export class EngravingSettings {
public directionsScale = 0.6;

/**
* The spacing between displaced displaced note heads
* in case of multi-voice note head overlaps.
* The spacing between displaced displaced note heads
* in case of multi-voice note head overlaps.
*/
public multiVoiceDisplacedNoteHeadSpacing = 0;

/**
* Calculates the stem height for a note of the given duration.
* @param duration The duration to calculate the height respecting flag sizes.
* @param hasFlag True if we need to respect flags, false if we have beams.
* @returns The total stem height
*/
public getStemLength(duration: Duration, hasFlag: boolean) {
return this.standardStemLength + (hasFlag ? this.stemFlagOffsets.get(duration)! : 0);
}

/**
* The space needed by flags on the stem-side from top to bottom to place.
*/
public stemFlagHeight: Map<Duration, number> = new Map<Duration, number>();

// Idea: maybe we can encode and pack this large metadata into a more compact format (e.g. BSON or a custom binary blob?)
// This metadata below is updated automatically from the bravura_metadata.json via npm script

Expand Down
1 change: 1 addition & 0 deletions packages/alphatab/src/generated/DisplaySettingsJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ export interface DisplaySettingsJson {
*
* * 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.
*/
systemsLayoutMode?: SystemsLayoutMode | keyof typeof SystemsLayoutMode | Lowercase<keyof typeof SystemsLayoutMode>;
}
1 change: 1 addition & 0 deletions packages/alphatab/src/generated/EngravingSettingsCloner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export class EngravingSettingsCloner {
clone.tuningGlyphStringRowPadding = original.tuningGlyphStringRowPadding;
clone.directionsScale = original.directionsScale;
clone.multiVoiceDisplacedNoteHeadSpacing = original.multiVoiceDisplacedNoteHeadSpacing;
clone.stemFlagHeight = new Map(original.stemFlagHeight);
return clone;
}
}
4 changes: 4 additions & 0 deletions packages/alphatab/src/generated/EngravingSettingsJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,4 +400,8 @@ export interface EngravingSettingsJson {
* in case of multi-voice note head overlaps.
*/
multiVoiceDisplacedNoteHeadSpacing?: number;
/**
* The space needed by flags on the stem-side from top to bottom to place.
*/
stemFlagHeight?: Map<Duration | keyof typeof Duration | Lowercase<keyof typeof Duration>, number>;
}
13 changes: 13 additions & 0 deletions packages/alphatab/src/generated/EngravingSettingsSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ export class EngravingSettingsSerializer {
o.set("tuningglyphstringrowpadding", obj.tuningGlyphStringRowPadding);
o.set("directionsscale", obj.directionsScale);
o.set("multivoicedisplacednoteheadspacing", obj.multiVoiceDisplacedNoteHeadSpacing);
{
const m = new Map<string, unknown>();
o.set("stemflagheight", m);
for (const [k, v] of obj.stemFlagHeight!) {
m.set(k.toString(), v);
}
}
return o;
}
public static setProperty(obj: EngravingSettings, property: string, v: unknown): boolean {
Expand Down Expand Up @@ -436,6 +443,12 @@ export class EngravingSettingsSerializer {
case "multivoicedisplacednoteheadspacing":
obj.multiVoiceDisplacedNoteHeadSpacing = v! as number;
return true;
case "stemflagheight":
obj.stemFlagHeight = new Map<Duration, number>();
JsonHelper.forEach(v, (v, k) => {
obj.stemFlagHeight.set(JsonHelper.parseEnum<Duration>(k, Duration)!, v as number);
});
return true;
}
return false;
}
Expand Down
4 changes: 0 additions & 4 deletions packages/alphatab/src/importer/AlphaTexImporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,6 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter
// as long we have some nodes, we can already start semantically
// validating and using them

if (scoreNode.bars.length === 0) {
throw new UnsupportedFormatError('No alphaTex data found');
}

this._bars(scoreNode);

if (this.semanticDiagnostics.hasErrors) {
Expand Down
7 changes: 1 addition & 6 deletions packages/alphatab/src/rendering/BarRendererBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ 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 @@ -22,11 +21,11 @@ import type { BarLayoutingInfo } from '@coderline/alphatab/rendering/staves/BarL
import type { RenderStaff } from '@coderline/alphatab/rendering/staves/RenderStaff';
import { BarBounds } from '@coderline/alphatab/rendering/utils/BarBounds';
import { BarHelpers } from '@coderline/alphatab/rendering/utils/BarHelpers';
import { BeamDirection } from '@coderline/alphatab/rendering/utils/BeamDirection';
import type { BeamingHelper } from '@coderline/alphatab/rendering/utils/BeamingHelper';
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 @@ -730,8 +729,4 @@ export class BarRendererBase {
public completeBeamingHelper(_helper: BeamingHelper) {
// nothing by default
}

public getBeatDirection(beat: Beat): BeamDirection {
return this.helpers.getBeamingHelperForBeat(beat)?.direction ?? BeamDirection.Up;
}
}
9 changes: 8 additions & 1 deletion packages/alphatab/src/rendering/EffectBandContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,23 @@ export class EffectBandContainer {
}

let y: number = 0;
// TODO. activate padding
// const paddingTop = this._isTopContainer ? 0 : this._renderer.settings.display.effectBandPaddingBottom;
// const paddingBottom = this._isTopContainer ? this._renderer.settings.display.effectBandPaddingBottom : 0;
const paddingTop = 0;
const paddingBottom = this._renderer.settings.display.effectBandPaddingBottom;

for (const slot of this._effectBandSizingInfo.slots) {
slot.shared.y = y;
for (const band of slot.bands) {
y += paddingTop;
band.y = y;
if (finalize) {
band.finalizeBand();
}
band.height = slot.shared.height;
}
y += slot.shared.height + this._renderer.settings.display.effectBandPaddingBottom;
y += slot.shared.height + paddingBottom;
}
y = Math.ceil(y);

Expand Down
Loading
Loading