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 scripts/setup-playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ const fs = require('fs-extra');

const src = `${__dirname}/../playground-template`
const dest = `${__dirname}/../playground`
fs.copySync(src, dest);
fs.copySync(src, dest, { overwrite: false });
31 changes: 31 additions & 0 deletions src/importer/GpifParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { XmlDocument } from '@src/xml/XmlDocument';
import { XmlNode, XmlNodeType } from '@src/xml/XmlNode';
import { MidiUtils } from '@src/midi/MidiUtils';
import { BeamDirection } from '@src/rendering/utils/BeamDirection';
import { NoteAccidentalMode } from '@src/model/NoteAccidentalMode';

/**
* This structure represents a duration within a gpif
Expand Down Expand Up @@ -1534,6 +1535,9 @@ export class GpifParser {
case 'Tone':
note.tone = parseInt(c.findChildElement('Step')!.innerText);
break;
case 'ConcertPitch':
this.parseConcertPitch(c, note);
break;
case 'Bended':
isBended = true;
break;
Expand Down Expand Up @@ -1640,6 +1644,33 @@ export class GpifParser {
note.addBendPoint(bendDestination);
}
}
private parseConcertPitch(node: XmlNode, note: Note) {
const pitch = node.findChildElement('Pitch');
if (pitch) {
for (let c of pitch.childNodes) {
if (c.nodeType === XmlNodeType.Element) {
switch (c.localName) {
case 'Accidental':
switch (c.innerText) {
case 'x':
note.accidentalMode = NoteAccidentalMode.ForceDoubleSharp;
break;
case '#':
note.accidentalMode = NoteAccidentalMode.ForceSharp;
break;
case 'b':
note.accidentalMode = NoteAccidentalMode.ForceFlat;
break;
case 'bb':
note.accidentalMode = NoteAccidentalMode.ForceDoubleFlat;
break;
}
break;
}
}
}
}
}

private toBendValue(gpxValue: number): number {
return (gpxValue * GpifParser.BendPointValueFactor) | 0;
Expand Down
10 changes: 9 additions & 1 deletion src/model/AccidentalType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,13 @@ export enum AccidentalType {
/**
* Flat for smear bends
*/
FlatQuarterNoteUp
FlatQuarterNoteUp,
/**
* Double Sharp, indicated by an 'x'
*/
DoubleSharp,
/**
* Double Flat, indicated by 'bb'
*/
DoubleFlat
}
32 changes: 19 additions & 13 deletions src/model/Note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,24 +434,23 @@ export class Note {
return 0;
}

public get displayValue(): number {
let noteValue: number = this.displayValueWithoutBend;
public get initialBendValue(): number {
if (this.hasBend) {
noteValue += (this.bendPoints[0].value / 2) | 0;
return (this.bendPoints[0].value / 2) | 0;
} else if (this.bendOrigin) {
noteValue += (this.bendOrigin.bendPoints[this.bendOrigin.bendPoints.length - 1].value / 2) | 0;
return (this.bendOrigin.bendPoints[this.bendOrigin.bendPoints.length - 1].value / 2) | 0;
} else if (this.isTieDestination && this.tieOrigin!.bendOrigin) {
noteValue +=
(this.tieOrigin!.bendOrigin.bendPoints[this.tieOrigin!.bendOrigin.bendPoints.length - 1].value / 2) | 0;
return (this.tieOrigin!.bendOrigin.bendPoints[this.tieOrigin!.bendOrigin.bendPoints.length - 1].value / 2) | 0;
} else if (this.beat.hasWhammyBar) {
noteValue += (this.beat.whammyBarPoints[0].value / 2) | 0;
return (this.beat.whammyBarPoints[0].value / 2) | 0;
} else if (this.beat.isContinuedWhammy) {
noteValue +=
(this.beat.previousBeat!.whammyBarPoints[this.beat.previousBeat!.whammyBarPoints.length - 1].value /
2) |
0;
return (this.beat.previousBeat!.whammyBarPoints[this.beat.previousBeat!.whammyBarPoints.length - 1].value / 2) | 0;
}
return noteValue;
return 0;
}

public get displayValue(): number {
return this.displayValueWithoutBend + this.initialBendValue;
}

public get displayValueWithoutBend(): number {
Expand Down Expand Up @@ -507,7 +506,7 @@ export class Note {
if (this.beat.isContinuedWhammy) {
return (
this.beat.previousBeat!.whammyBarPoints[this.beat.previousBeat!.whammyBarPoints.length - 1].value %
2 !==
2 !==
0
);
}
Expand Down Expand Up @@ -575,6 +574,7 @@ export class Note {
public finish(settings: Settings): void {
let nextNoteOnLine: Lazy<Note | null> = new Lazy<Note | null>(() => Note.nextNoteOnSameLine(this));
let isSongBook: boolean = settings && settings.notation.notationMode === NotationMode.SongBook;

// connect ties
if (this.isTieDestination) {
this.chain();
Expand Down Expand Up @@ -717,6 +717,12 @@ export class Note {
} else if (this.bendPoints.length === 0) {
this.bendType = BendType.None;
}

// initial bend pitch offsets and forced accidentals don't play well together
// we reset it
if(this.initialBendValue > 0) {
this.accidentalMode = NoteAccidentalMode.Default;
}
}

private static readonly MaxOffsetForSameLineSearch: number = 3;
Expand Down
14 changes: 13 additions & 1 deletion src/model/NoteAccidentalMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export enum NoteAccidentalMode {
* If the default behavior calculates a Sharp, use flat instead (and vice versa).
*/
SwapAccidentals,
/**
* This will try to ensure that no accidental is shown.
*/
ForceNone,
/**
* This will move the note one line down and applies a Naturalize.
*/
Expand All @@ -18,8 +22,16 @@ export enum NoteAccidentalMode {
* This will move the note one line down and applies a Sharp.
*/
ForceSharp,
/**
* This will move the note to be shown 2 half-notes deeper with a double sharp symbol
*/
ForceDoubleSharp,
/**
* This will move the note one line up and applies a Flat.
*/
ForceFlat
ForceFlat,
/**
* This will move the note two half notes up with a double flag symbol.
*/
ForceDoubleFlat,
}
17 changes: 15 additions & 2 deletions src/rendering/glyphs/AccidentalGlyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph';

export class AccidentalGlyph extends MusicFontGlyph {
private _isGrace: boolean;
private _accidentalType: AccidentalType;

public constructor(x: number, y: number, accidentalType: AccidentalType, isGrace: boolean = false) {
super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, AccidentalGlyph.getMusicSymbol(accidentalType));
this._isGrace = false;
this._isGrace = isGrace;
this._accidentalType = accidentalType;
}

private static getMusicSymbol(accidentalType: AccidentalType): MusicFontSymbol {
Expand All @@ -26,11 +27,23 @@ export class AccidentalGlyph extends MusicFontGlyph {
return MusicFontSymbol.AccidentalQuarterToneSharpArrowUp;
case AccidentalType.FlatQuarterNoteUp:
return MusicFontSymbol.AccidentalQuarterToneFlatArrowUp;
case AccidentalType.DoubleSharp:
return MusicFontSymbol.AccidentalDoubleSharp;
case AccidentalType.DoubleFlat:
return MusicFontSymbol.AccidentalDoubleFlat;
}
return MusicFontSymbol.None;
}

public doLayout(): void {
this.width = 8 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale;
switch (this._accidentalType) {
case AccidentalType.DoubleFlat:
this.width = 18;
break;
default:
this.width = 8;
break;
}
this.width = this.width * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale;
}
}
53 changes: 36 additions & 17 deletions src/rendering/glyphs/AccidentalGroupGlyph.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { Glyph } from '@src/rendering/glyphs/Glyph';
import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup';
import { AccidentalGlyph } from './AccidentalGlyph';

export class AccidentalGroupGlyph extends GlyphGroup {
private static readonly NonReserved: number = -3000;
class AccidentalColumnInfo {
public x: number = 0;
public y: number = -3000;
public width: number = 0;
}

public constructor() {
export class AccidentalGroupGlyph extends GlyphGroup {
private _isGrace:boolean;
public constructor(isGrace:boolean) {
super(0, 0);
this._isGrace = isGrace;
}

public doLayout(): void {
if (!this.glyphs) {
if (!this.glyphs || this.glyphs.length === 0) {
this.width = 0;
return;
}
Expand All @@ -25,42 +32,54 @@ export class AccidentalGroupGlyph extends GlyphGroup {
}
return 0;
});

// defines the reserved y position of the columns
let columns: number[] = [];
columns.push(AccidentalGroupGlyph.NonReserved);
let accidentalSize: number = 21 * this.scale;
let columns: AccidentalColumnInfo[] = [];
columns.push(new AccidentalColumnInfo());
let accidentalHeight: number = 21 * this.scale;
for (let i: number = 0, j: number = this.glyphs.length; i < j; i++) {
let g: Glyph = this.glyphs[i];
let g = this.glyphs[i] as AccidentalGlyph;
g.renderer = this.renderer;
g.doLayout();
// find column where glyph fits into
// as long the glyph does not fit into the current column
let gColumn: number = 0;
while (columns[gColumn] > g.y) {
while (columns[gColumn].y > g.y) {
// move to next column
gColumn++;
// and create the new column if needed
if (gColumn === columns.length) {
columns.push(AccidentalGroupGlyph.NonReserved);
columns.push(new AccidentalColumnInfo());
}
}
// temporary save column as X
g.x = gColumn;
columns[gColumn] = g.y + accidentalSize;
columns[gColumn].y = g.y + accidentalHeight;
if (columns[gColumn].width < g.width) {
columns[gColumn].width = g.width;
}
}

//
// Place accidentals in columns
//
let columnWidth: number = 8 * this.scale;
let padding: number = 2 * this.scale;
if (this.glyphs.length === 0) {
this.width = 0;
} else {
this.width = padding + columnWidth * columns.length;
this.width = 0;
for (const column of columns) {
this.width += column.width;
column.x = this.width;
}
this.width += padding;

for (let i: number = 0, j: number = this.glyphs.length; i < j; i++) {
let g: Glyph = this.glyphs[i];
g.x = padding + (this.width - (g.x + 1) * columnWidth);

const column = columns[g.x];
g.x = padding + (this.width - column.x);
}

if(this._isGrace) {
this.width += padding;
}
}
}
2 changes: 1 addition & 1 deletion src/rendering/glyphs/BendNoteHeadGroupGlyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class BendNoteHeadGroupGlyph extends ScoreNoteChordGlyphBase {
private _beat: Beat;
private _showParenthesis: boolean = false;
private _noteValueLookup: Map<number, Glyph> = new Map();
private _accidentals: AccidentalGroupGlyph = new AccidentalGroupGlyph();
private _accidentals: AccidentalGroupGlyph = new AccidentalGroupGlyph(true);
private _preNoteParenthesis: GhostNoteContainerGlyph | null = null;
private _postNoteParenthesis: GhostNoteContainerGlyph | null = null;
public isEmpty: boolean = true;
Expand Down
2 changes: 2 additions & 0 deletions src/rendering/glyphs/MusicFontSymbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export enum MusicFontSymbol {
AccidentalFlat = 0xe260,
AccidentalNatural = 0xe261,
AccidentalSharp = 0xe262,
AccidentalDoubleSharp = 0xe263,
AccidentalDoubleFlat = 0xe264,
AccidentalQuarterToneFlatArrowUp = 0xe270,
AccidentalQuarterToneSharpArrowUp = 0xe274,
AccidentalQuarterToneNaturalArrowUp = 0xe272,
Expand Down
2 changes: 1 addition & 1 deletion src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class ScoreBeatPreNotesGlyph extends BeatGlyphBase {

public doLayout(): void {
if (!this.container.beat.isRest) {
let accidentals: AccidentalGroupGlyph = new AccidentalGroupGlyph();
let accidentals: AccidentalGroupGlyph = new AccidentalGroupGlyph(this.container.beat.graceType != GraceType.None);
let ghost: GhostNoteContainerGlyph = new GhostNoteContainerGlyph(true);
ghost.renderer = this.renderer;
this._prebends = new BendNoteHeadGroupGlyph(this.container.beat, true);
Expand Down
Loading