UNPKG

@coderline/alphatab

Version:

alphaTab is a music notation and guitar tablature rendering library

1,573 lines (1,552 loc) 2.64 MB
/*! * alphaTab v1.6.3 (, build 22) * * Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Integrated Libraries: * * Library: TinySoundFont * License: MIT * Copyright: Copyright (C) 2017, 2018 Bernhard Schelling * URL: https://github.com/schellingb/TinySoundFont * Purpose: SoundFont loading and Audio Synthesis * * Library: SFZero * License: MIT * Copyright: Copyright (C) 2012 Steve Folta () * URL: https://github.com/stevefolta/SFZero * Purpose: TinySoundFont is based on SFZEro * * Library: Haxe Standard Library * License: MIT * Copyright: Copyright (C)2005-2025 Haxe Foundation * URL: https://github.com/HaxeFoundation/haxe/tree/development/std * Purpose: XML Parser & Zip Inflate Algorithm * * Library: SharpZipLib * License: MIT * Copyright: Copyright © 2000-2018 SharpZipLib Contributors * URL: https://github.com/icsharpcode/SharpZipLib * Purpose: Zip Deflate Algorithm for writing compressed Zips * * Library: NVorbis * License: MIT * Copyright: Copyright (c) 2020 Andrew Ward * URL: https://github.com/NVorbis/NVorbis * Purpose: Vorbis Stream Decoding * * Library: libvorbis * License: BSD-3-Clause * Copyright: Copyright (c) 2002-2020 Xiph.org Foundation * URL: https://github.com/xiph/vorbis * Purpose: NVorbis adopted some code from libvorbis. * * @preserve * @license */ /** * A very basic polyfill of the ResizeObserver which triggers * a the callback on window resize for all registered targets. * @target web */ class ResizeObserverPolyfill { constructor(callback) { this._targets = new Set(); this._callback = callback; window.addEventListener('resize', this.onWindowResize.bind(this), false); } observe(target) { this._targets.add(target); } unobserve(target) { this._targets.delete(target); } disconnect() { this._targets.clear(); } onWindowResize() { const entries = []; for (const t of this._targets) { entries.push({ target: t, // not used by alphaTab contentRect: undefined, borderBoxSize: undefined, contentBoxSize: [], devicePixelContentBoxSize: [] }); } this._callback(entries, this); } } /** * A polyfill of the InsersectionObserver * @target web */ class IntersectionObserverPolyfill { constructor(callback) { this._elements = []; let timer = null; const oldCheck = this.check.bind(this); this.check = () => { if (!timer) { timer = setTimeout(() => { oldCheck(); timer = null; }, 100); } }; this._callback = callback; window.addEventListener('resize', this.check, true); document.addEventListener('scroll', this.check, true); } observe(target) { if (this._elements.indexOf(target) >= 0) { return; } this._elements.push(target); this.check(); } unobserve(target) { this._elements = this._elements.filter(item => { return item !== target; }); } check() { const entries = []; for (const element of this._elements) { const rect = element.getBoundingClientRect(); const isVisible = rect.top + rect.height >= 0 && rect.top <= window.innerHeight && rect.left + rect.width >= 0 && rect.left <= window.innerWidth; if (isVisible) { entries.push({ target: element, isIntersecting: true }); } } if (entries.length) { this._callback(entries, this); } } } /*@target web*/ (() => { if (typeof Symbol.dispose === 'undefined') { Symbol.dispose = Symbol('Symbol.dispose'); } if (typeof window !== 'undefined') { // ResizeObserver API does not yet exist so long on Safari (only start 2020 with iOS Safari 13.7 and Desktop 13.1) // so we better add a polyfill for it if (!('ResizeObserver' in globalThis)) { globalThis.ResizeObserver = ResizeObserverPolyfill; } // IntersectionObserver API does not on older iOS versions // so we better add a polyfill for it if (!('IntersectionObserver' in globalThis)) { globalThis.IntersectionObserver = IntersectionObserverPolyfill; } if (!('replaceChildren' in Element.prototype)) { Element.prototype.replaceChildren = function (...nodes) { this.innerHTML = ''; this.append(...nodes); }; Document.prototype.replaceChildren = Element.prototype.replaceChildren; DocumentFragment.prototype.replaceChildren = Element.prototype.replaceChildren; } } if (!('replaceAll' in String.prototype)) { String.prototype.replaceAll = function (str, newStr) { return this.replace(new RegExp(str, 'g'), newStr); }; } })(); /** * Lists all layout modes that are supported. */ var LayoutMode; (function (LayoutMode) { /** * The bars are aligned in an [vertically endless page-style fashion](https://alphatab.net/docs/showcase/layouts#page-layout) */ LayoutMode[LayoutMode["Page"] = 0] = "Page"; /** * Bars are aligned horizontally in [one horizontally endless system (row)](https://alphatab.net/docs/showcase/layouts#horizontal-layout) */ LayoutMode[LayoutMode["Horizontal"] = 1] = "Horizontal"; })(LayoutMode || (LayoutMode = {})); /** * Lists all stave profiles controlling which staves are shown. */ var StaveProfile; (function (StaveProfile) { /** * The profile is auto detected by the track configurations. */ StaveProfile[StaveProfile["Default"] = 0] = "Default"; /** * Standard music notation and guitar tablature are rendered. */ StaveProfile[StaveProfile["ScoreTab"] = 1] = "ScoreTab"; /** * Only standard music notation is rendered. */ StaveProfile[StaveProfile["Score"] = 2] = "Score"; /** * Only guitar tablature is rendered. */ StaveProfile[StaveProfile["Tab"] = 3] = "Tab"; /** * Only guitar tablature is rendered, but also rests and time signatures are not shown. * This profile is typically used in multi-track scenarios. */ StaveProfile[StaveProfile["TabMixed"] = 4] = "TabMixed"; })(StaveProfile || (StaveProfile = {})); /** * This public class provides names for all general midi instruments. */ class GeneralMidi { static getValue(name) { if (!GeneralMidi._values) { GeneralMidi._values = new Map(); } name = name.toLowerCase().replaceAll(' ', ''); return GeneralMidi._values.has(name) ? GeneralMidi._values.get(name) : 0; } static isPiano(program) { return program <= 7 || (program >= 16 && program <= 23); } static isGuitar(program) { return (program >= 24 && program <= 39) || program === 105 || program === 43; } } GeneralMidi._values = new Map([ ['acousticgrandpiano', 0], ['brightacousticpiano', 1], ['electricgrandpiano', 2], ['honkytonkpiano', 3], ['electricpiano1', 4], ['electricpiano2', 5], ['harpsichord', 6], ['clavinet', 7], ['celesta', 8], ['glockenspiel', 9], ['musicbox', 10], ['vibraphone', 11], ['marimba', 12], ['xylophone', 13], ['tubularbells', 14], ['dulcimer', 15], ['drawbarorgan', 16], ['percussiveorgan', 17], ['rockorgan', 18], ['churchorgan', 19], ['reedorgan', 20], ['accordion', 21], ['harmonica', 22], ['tangoaccordion', 23], ['acousticguitarnylon', 24], ['acousticguitarsteel', 25], ['electricguitarjazz', 26], ['electricguitarclean', 27], ['electricguitarmuted', 28], ['overdrivenguitar', 29], ['distortionguitar', 30], ['guitarharmonics', 31], ['acousticbass', 32], ['electricbassfinger', 33], ['electricbasspick', 34], ['fretlessbass', 35], ['slapbass1', 36], ['slapbass2', 37], ['synthbass1', 38], ['synthbass2', 39], ['violin', 40], ['viola', 41], ['cello', 42], ['contrabass', 43], ['tremolostrings', 44], ['pizzicatostrings', 45], ['orchestralharp', 46], ['timpani', 47], ['stringensemble1', 48], ['stringensemble2', 49], ['synthstrings1', 50], ['synthstrings2', 51], ['choiraahs', 52], ['voiceoohs', 53], ['synthvoice', 54], ['orchestrahit', 55], ['trumpet', 56], ['trombone', 57], ['tuba', 58], ['mutedtrumpet', 59], ['frenchhorn', 60], ['brasssection', 61], ['synthbrass1', 62], ['synthbrass2', 63], ['sopranosax', 64], ['altosax', 65], ['tenorsax', 66], ['baritonesax', 67], ['oboe', 68], ['englishhorn', 69], ['bassoon', 70], ['clarinet', 71], ['piccolo', 72], ['flute', 73], ['recorder', 74], ['panflute', 75], ['blownbottle', 76], ['shakuhachi', 77], ['whistle', 78], ['ocarina', 79], ['lead1square', 80], ['lead2sawtooth', 81], ['lead3calliope', 82], ['lead4chiff', 83], ['lead5charang', 84], ['lead6voice', 85], ['lead7fifths', 86], ['lead8bassandlead', 87], ['pad1newage', 88], ['pad2warm', 89], ['pad3polysynth', 90], ['pad4choir', 91], ['pad5bowed', 92], ['pad6metallic', 93], ['pad7halo', 94], ['pad8sweep', 95], ['fx1rain', 96], ['fx2soundtrack', 97], ['fx3crystal', 98], ['fx4atmosphere', 99], ['fx5brightness', 100], ['fx6goblins', 101], ['fx7echoes', 102], ['fx8scifi', 103], ['sitar', 104], ['banjo', 105], ['shamisen', 106], ['koto', 107], ['kalimba', 108], ['bagpipe', 109], ['fiddle', 110], ['shanai', 111], ['tinklebell', 112], ['agogo', 113], ['steeldrums', 114], ['woodblock', 115], ['taikodrum', 116], ['melodictom', 117], ['synthdrum', 118], ['reversecymbal', 119], ['guitarfretnoise', 120], ['breathnoise', 121], ['seashore', 122], ['birdtweet', 123], ['telephonering', 124], ['helicopter', 125], ['applause', 126], ['gunshot', 127] ]); /** * Lists the different modes on how the brackets/braces are drawn and extended. */ var BracketExtendMode; (function (BracketExtendMode) { /** * Do not draw brackets */ BracketExtendMode[BracketExtendMode["NoBrackets"] = 0] = "NoBrackets"; /** * Groups staves into bracket (or braces for grand staff). */ BracketExtendMode[BracketExtendMode["GroupStaves"] = 1] = "GroupStaves"; /** * Groups similar instruments in multi-track rendering into brackets. * The braces of tracks with grand-staffs break any brackets. * Similar instruments means actually the same "midi program". No custom grouping is currently done. */ BracketExtendMode[BracketExtendMode["GroupSimilarInstruments"] = 2] = "GroupSimilarInstruments"; })(BracketExtendMode || (BracketExtendMode = {})); /** * Lists the different policies on how to display the track names. */ var TrackNamePolicy; (function (TrackNamePolicy) { /** * Track names are hidden everywhere. */ TrackNamePolicy[TrackNamePolicy["Hidden"] = 0] = "Hidden"; /** * Track names are displayed on the first system. */ TrackNamePolicy[TrackNamePolicy["FirstSystem"] = 1] = "FirstSystem"; /** * Track names are displayed on all systems. */ TrackNamePolicy[TrackNamePolicy["AllSystems"] = 2] = "AllSystems"; })(TrackNamePolicy || (TrackNamePolicy = {})); /** * Lists the different modes what text to display for track names. */ var TrackNameMode; (function (TrackNameMode) { /** * Full track names are displayed {@link Track.name} */ TrackNameMode[TrackNameMode["FullName"] = 0] = "FullName"; /** * Short Track names (abbreviations) are displayed {@link Track.shortName} */ TrackNameMode[TrackNameMode["ShortName"] = 1] = "ShortName"; })(TrackNameMode || (TrackNameMode = {})); /** * Lists the different orientations modes how to render the track names. */ var TrackNameOrientation; (function (TrackNameOrientation) { /** * Text is shown horizontally (left-to-right) */ TrackNameOrientation[TrackNameOrientation["Horizontal"] = 0] = "Horizontal"; /** * Vertically rotated (bottom-to-top) */ TrackNameOrientation[TrackNameOrientation["Vertical"] = 1] = "Vertical"; })(TrackNameOrientation || (TrackNameOrientation = {})); /** * This class represents the rendering stylesheet. * It contains settings which control the display of the score when rendered. * @json * @json_strict */ class RenderStylesheet { constructor() { /** * Whether dynamics are hidden. */ this.hideDynamics = false; /** * The mode in which brackets and braces are drawn. */ this.bracketExtendMode = BracketExtendMode.GroupStaves; /** * Whether to draw the // sign to separate systems. */ this.useSystemSignSeparator = false; /** * Whether to show the tuning. */ this.globalDisplayTuning = true; /** * Whether to show the tuning.(per-track) */ this.perTrackDisplayTuning = null; /** * Whether to show the chord diagrams on top. */ this.globalDisplayChordDiagramsOnTop = true; /** * Whether to show the chord diagrams on top. (per-track) */ this.perTrackChordDiagramsOnTop = null; /** * The policy where to show track names when a single track is rendered. */ this.singleTrackTrackNamePolicy = TrackNamePolicy.FirstSystem; /** * The policy where to show track names when a multiple tracks are rendered. */ this.multiTrackTrackNamePolicy = TrackNamePolicy.FirstSystem; /** * The mode what text to display for the track name on the first system */ this.firstSystemTrackNameMode = TrackNameMode.ShortName; /** * The mode what text to display for the track name on the first system */ this.otherSystemsTrackNameMode = TrackNameMode.ShortName; /** * The orientation of the the track names on the first system */ this.firstSystemTrackNameOrientation = TrackNameOrientation.Vertical; /** * The orientation of the the track names on other systems */ this.otherSystemsTrackNameOrientation = TrackNameOrientation.Vertical; /** * If multi track: Whether to render multiple subsequent empty (or rest-only) bars together as multi-bar rest. */ this.multiTrackMultiBarRest = false; /** * If single track: Whether to render multiple subsequent empty (or rest-only) bars together as multi-bar rest. */ this.perTrackMultiBarRest = null; } } /** * This public class can store the information about a group of measures which are repeated */ class RepeatGroup { constructor() { /** * All masterbars repeated within this group */ this.masterBars = []; /** * the masterbars which opens the group. */ this.opening = null; /** * a list of masterbars which close the group. */ this.closings = []; /** * true if the repeat group was closed well */ this.isClosed = false; } /** * a list of masterbars which open the group. * @deprecated There can only be one opening, use the opening property instead */ get openings() { const opening = this.opening; return opening ? [opening] : []; } /** * Gets whether this repeat group is really opened as a repeat. */ get isOpened() { return this.opening?.isRepeatStart === true; } addMasterBar(masterBar) { if (this.opening === null) { this.opening = masterBar; } this.masterBars.push(masterBar); masterBar.repeatGroup = this; if (masterBar.isRepeatEnd) { this.closings.push(masterBar); this.isClosed = true; } } } /** * Defines the custom styles for an element in the music sheet (like bars, voices, notes etc). */ class ElementStyle { constructor() { /** * Changes the color of the specified sub-element within the element this style container belongs to. * Null indicates that a certain element should use the default color from {@link RenderingResources} * even if some "higher level" element changes colors. */ this.colors = new Map(); // TODO: replace NotationSettings.elements by adding a visibility here? } } /** * This public enumeration lists all supported Clefs. */ var Clef; (function (Clef) { /** * Neutral clef. */ Clef[Clef["Neutral"] = 0] = "Neutral"; /** * C3 clef */ Clef[Clef["C3"] = 1] = "C3"; /** * C4 clef */ Clef[Clef["C4"] = 2] = "C4"; /** * F4 clef */ Clef[Clef["F4"] = 3] = "F4"; /** * G2 clef */ Clef[Clef["G2"] = 4] = "G2"; })(Clef || (Clef = {})); /** * Lists all ottavia. */ var Ottavia; (function (Ottavia) { /** * 2 octaves higher */ Ottavia[Ottavia["_15ma"] = 0] = "_15ma"; /** * 1 octave higher */ Ottavia[Ottavia["_8va"] = 1] = "_8va"; /** * Normal */ Ottavia[Ottavia["Regular"] = 2] = "Regular"; /** * 1 octave lower */ Ottavia[Ottavia["_8vb"] = 3] = "_8vb"; /** * 2 octaves lower. */ Ottavia[Ottavia["_15mb"] = 4] = "_15mb"; })(Ottavia || (Ottavia = {})); /** * Lists all simile mark types as they are assigned to bars. */ var SimileMark; (function (SimileMark) { /** * No simile mark is applied */ SimileMark[SimileMark["None"] = 0] = "None"; /** * A simple simile mark. The previous bar is repeated. */ SimileMark[SimileMark["Simple"] = 1] = "Simple"; /** * A double simile mark. This value is assigned to the first * bar of the 2 repeat bars. */ SimileMark[SimileMark["FirstOfDouble"] = 2] = "FirstOfDouble"; /** * A double simile mark. This value is assigned to the second * bar of the 2 repeat bars. */ SimileMark[SimileMark["SecondOfDouble"] = 3] = "SecondOfDouble"; })(SimileMark || (SimileMark = {})); /** * This public enumeration lists all available key signatures */ var KeySignature; (function (KeySignature) { /** * Cb (7 flats) */ KeySignature[KeySignature["Cb"] = -7] = "Cb"; /** * Gb (6 flats) */ KeySignature[KeySignature["Gb"] = -6] = "Gb"; /** * Db (5 flats) */ KeySignature[KeySignature["Db"] = -5] = "Db"; /** * Ab (4 flats) */ KeySignature[KeySignature["Ab"] = -4] = "Ab"; /** * Eb (3 flats) */ KeySignature[KeySignature["Eb"] = -3] = "Eb"; /** * Bb (2 flats) */ KeySignature[KeySignature["Bb"] = -2] = "Bb"; /** * F (1 flat) */ KeySignature[KeySignature["F"] = -1] = "F"; /** * C (no signs) */ KeySignature[KeySignature["C"] = 0] = "C"; /** * G (1 sharp) */ KeySignature[KeySignature["G"] = 1] = "G"; /** * D (2 sharp) */ KeySignature[KeySignature["D"] = 2] = "D"; /** * A (3 sharp) */ KeySignature[KeySignature["A"] = 3] = "A"; /** * E (4 sharp) */ KeySignature[KeySignature["E"] = 4] = "E"; /** * B (5 sharp) */ KeySignature[KeySignature["B"] = 5] = "B"; /** * F# (6 sharp) */ KeySignature[KeySignature["FSharp"] = 6] = "FSharp"; /** * C# (7 sharp) */ KeySignature[KeySignature["CSharp"] = 7] = "CSharp"; })(KeySignature || (KeySignature = {})); /** * This public enumeration lists all available types of KeySignatures */ var KeySignatureType; (function (KeySignatureType) { /** * Major */ KeySignatureType[KeySignatureType["Major"] = 0] = "Major"; /** * Minor */ KeySignatureType[KeySignatureType["Minor"] = 1] = "Minor"; })(KeySignatureType || (KeySignatureType = {})); /** * The different pedal marker types. */ var SustainPedalMarkerType; (function (SustainPedalMarkerType) { /** * Indicates that the pedal should be pressed from this time on. */ SustainPedalMarkerType[SustainPedalMarkerType["Down"] = 0] = "Down"; /** * Indicates that the pedal should be held on this marker (used when the pedal is held for the whole bar) */ SustainPedalMarkerType[SustainPedalMarkerType["Hold"] = 1] = "Hold"; /** * indicates that the pedal should be lifted up at this time. */ SustainPedalMarkerType[SustainPedalMarkerType["Up"] = 2] = "Up"; })(SustainPedalMarkerType || (SustainPedalMarkerType = {})); /** * A marker on whether a sustain pedal starts or ends. * @json * @json_strict */ class SustainPedalMarker { constructor() { /** * The relative position of pedal markers within the bar. */ this.ratioPosition = 0; /** * Whether what should be done with the pedal at this point */ this.pedalType = SustainPedalMarkerType.Down; /** * The next pedal marker for linking the related markers together to a "down -> hold -> up" or "down -> up" sequence. * Always null for "up" markers. * @json_ignore */ this.nextPedalMarker = null; /** * The previous pedal marker for linking the related markers together to a "down -> hold -> up" or "down -> up" sequence. * Always null for "down" markers. * @json_ignore */ this.previousPedalMarker = null; } } /** * Lists all graphical sub elements within a {@link Bar} which can be styled via {@link Bar.style} */ var BarSubElement; (function (BarSubElement) { /** * The repeat signs on the standard notation staff. */ BarSubElement[BarSubElement["StandardNotationRepeats"] = 0] = "StandardNotationRepeats"; /** * The repeat signs on the guitar tab staff. */ BarSubElement[BarSubElement["GuitarTabsRepeats"] = 1] = "GuitarTabsRepeats"; /** * The repeat signs on the slash staff. */ BarSubElement[BarSubElement["SlashRepeats"] = 2] = "SlashRepeats"; /** * The repeat signs on the numbered notation staff. */ BarSubElement[BarSubElement["NumberedRepeats"] = 3] = "NumberedRepeats"; /** * The bar numbers on the standard notation staff. */ BarSubElement[BarSubElement["StandardNotationBarNumber"] = 4] = "StandardNotationBarNumber"; /** * The bar numbers on the guitar tab staff. */ BarSubElement[BarSubElement["GuitarTabsBarNumber"] = 5] = "GuitarTabsBarNumber"; /** * The bar numbers on the slash staff. */ BarSubElement[BarSubElement["SlashBarNumber"] = 6] = "SlashBarNumber"; /** * The bar numbers on the numbered notation staff. */ BarSubElement[BarSubElement["NumberedBarNumber"] = 7] = "NumberedBarNumber"; /** * The bar lines on the standard notation staff. */ BarSubElement[BarSubElement["StandardNotationBarLines"] = 8] = "StandardNotationBarLines"; /** * The bar lines on the guitar tab staff. */ BarSubElement[BarSubElement["GuitarTabsBarLines"] = 9] = "GuitarTabsBarLines"; /** * The bar lines on the slash staff. */ BarSubElement[BarSubElement["SlashBarLines"] = 10] = "SlashBarLines"; /** * The bar lines on the numbered notation staff. */ BarSubElement[BarSubElement["NumberedBarLines"] = 11] = "NumberedBarLines"; /** * The clefs on the standard notation staff. */ BarSubElement[BarSubElement["StandardNotationClef"] = 12] = "StandardNotationClef"; /** * The clefs on the guitar tab staff. */ BarSubElement[BarSubElement["GuitarTabsClef"] = 13] = "GuitarTabsClef"; /** * The key signatures on the standard notation staff. */ BarSubElement[BarSubElement["StandardNotationKeySignature"] = 14] = "StandardNotationKeySignature"; /** * The key signatures on the numbered notation staff. */ BarSubElement[BarSubElement["NumberedKeySignature"] = 15] = "NumberedKeySignature"; /** * The time signatures on the standard notation staff. */ BarSubElement[BarSubElement["StandardNotationTimeSignature"] = 16] = "StandardNotationTimeSignature"; /** * The time signatures on the guitar tab staff. */ BarSubElement[BarSubElement["GuitarTabsTimeSignature"] = 17] = "GuitarTabsTimeSignature"; /** * The time signatures on the slash staff. */ BarSubElement[BarSubElement["SlashTimeSignature"] = 18] = "SlashTimeSignature"; /** * The time signature on the numbered notation staff. */ BarSubElement[BarSubElement["NumberedTimeSignature"] = 19] = "NumberedTimeSignature"; /** * The staff lines on the standard notation staff. */ BarSubElement[BarSubElement["StandardNotationStaffLine"] = 20] = "StandardNotationStaffLine"; /** * The staff lines on the guitar tab staff. */ BarSubElement[BarSubElement["GuitarTabsStaffLine"] = 21] = "GuitarTabsStaffLine"; /** * The staff lines on the slash staff. */ BarSubElement[BarSubElement["SlashStaffLine"] = 22] = "SlashStaffLine"; /** * The staff lines on the numbered notation staff. */ BarSubElement[BarSubElement["NumberedStaffLine"] = 23] = "NumberedStaffLine"; })(BarSubElement || (BarSubElement = {})); /** * Defines the custom styles for bars. * @json * @json_strict */ class BarStyle extends ElementStyle { } /** * Lists all bar line styles. */ var BarLineStyle; (function (BarLineStyle) { /** * No special custom line style, automatic handling (e.g. last bar might be LightHeavy) */ BarLineStyle[BarLineStyle["Automatic"] = 0] = "Automatic"; BarLineStyle[BarLineStyle["Dashed"] = 1] = "Dashed"; BarLineStyle[BarLineStyle["Dotted"] = 2] = "Dotted"; BarLineStyle[BarLineStyle["Heavy"] = 3] = "Heavy"; BarLineStyle[BarLineStyle["HeavyHeavy"] = 4] = "HeavyHeavy"; BarLineStyle[BarLineStyle["HeavyLight"] = 5] = "HeavyLight"; BarLineStyle[BarLineStyle["LightHeavy"] = 6] = "LightHeavy"; BarLineStyle[BarLineStyle["LightLight"] = 7] = "LightLight"; BarLineStyle[BarLineStyle["None"] = 8] = "None"; BarLineStyle[BarLineStyle["Regular"] = 9] = "Regular"; BarLineStyle[BarLineStyle["Short"] = 10] = "Short"; BarLineStyle[BarLineStyle["Tick"] = 11] = "Tick"; })(BarLineStyle || (BarLineStyle = {})); /** * A bar is a single block within a track, also known as Measure. * @json * @json_strict */ class Bar { constructor() { /** * Gets or sets the unique id of this bar. */ this.id = Bar._globalBarId++; /** * Gets or sets the zero-based index of this bar within the staff. * @json_ignore */ this.index = 0; /** * Gets or sets the next bar that comes after this bar. * @json_ignore */ this.nextBar = null; /** * Gets or sets the previous bar that comes before this bar. * @json_ignore */ this.previousBar = null; /** * Gets or sets the clef on this bar. */ this.clef = Clef.G2; /** * Gets or sets the ottava applied to the clef. */ this.clefOttava = Ottavia.Regular; /** * Gets or sets the list of voices contained in this bar. * @json_add addVoice */ this.voices = []; /** * Gets or sets the simile mark on this bar. */ this.simileMark = SimileMark.None; /** * Gets a value indicating whether this bar contains multiple voices with notes. * @json_ignore */ this.isMultiVoice = false; /** * A relative scale for the size of the bar when displayed. The scale is relative * within a single line (system). The sum of all scales in one line make the total width, * and then this individual scale gives the relative size. */ this.displayScale = 1; /** * An absolute width of the bar to use when displaying in single track display scenarios. */ this.displayWidth = -1; /** * The sustain pedal markers within this bar. */ this.sustainPedals = []; this._isEmpty = true; this._isRestOnly = true; /** * The bar line to draw on the left side of the bar. * @remarks * Note that the combination with {@link barLineRight} of the previous bar matters. * If this bar has a Regular/Automatic style but the previous bar is customized, no additional line is drawn by this bar. * If both bars have a custom style, both bar styles are drawn. */ this.barLineLeft = BarLineStyle.Automatic; /** * The bar line to draw on the right side of the bar. * @remarks * Note that the combination with {@link barLineLeft} of the next bar matters. * If this bar has a Regular/Automatic style but the next bar is customized, no additional line is drawn by this bar. * If both bars have a custom style, both bar styles are drawn. */ this.barLineRight = BarLineStyle.Automatic; /** * Gets or sets the key signature used on all bars. */ this.keySignature = KeySignature.C; /** * Gets or sets the type of key signature (major/minor) */ this.keySignatureType = KeySignatureType.Major; } /** * @internal */ static resetIds() { Bar._globalBarId = 0; } get masterBar() { return this.staff.track.score.masterBars[this.index]; } /** * Whether this bar is fully empty (not even having rests). */ get isEmpty() { return this._isEmpty; } /** * Whether this bar has any changes applied which are not related to the voices in it. * (e.g. new key signatures) */ get hasChanges() { if (this.index === 0) { return true; } const hasChangesToPrevious = this.keySignature !== this.previousBar.keySignature || this.keySignatureType !== this.previousBar.keySignatureType || this.clef !== this.previousBar.clef || this.clefOttava !== this.previousBar.clefOttava; if (hasChangesToPrevious) { return true; } return (this.simileMark !== SimileMark.None || this.sustainPedals.length > 0 || this.barLineLeft !== BarLineStyle.Automatic || this.barLineRight !== BarLineStyle.Automatic); } /** * Whether this bar is empty or has only rests. */ get isRestOnly() { return this._isRestOnly; } /** * The bar line to draw on the left side of the bar with an "automatic" type resolved to the actual one. * @param isFirstOfSystem Whether the bar is the first one in the system. */ getActualBarLineLeft(isFirstOfSystem) { return Bar.actualBarLine(this, false, isFirstOfSystem); } /** * The bar line to draw on the right side of the bar with an "automatic" type resolved to the actual one. * @param isFirstOfSystem Whether the bar is the first one in the system. */ getActualBarLineRight() { return Bar.actualBarLine(this, true, false /* not relevant */); } static automaticToActualType(masterBar, isRight, firstOfSystem) { let actualLineType; if (isRight) { if (masterBar.isRepeatEnd) { actualLineType = BarLineStyle.LightHeavy; } else if (!masterBar.nextMasterBar) { actualLineType = BarLineStyle.LightHeavy; } else if (masterBar.isFreeTime) { actualLineType = BarLineStyle.Dashed; } else if (masterBar.isDoubleBar) { actualLineType = BarLineStyle.LightLight; } else { actualLineType = BarLineStyle.Regular; } } else { if (masterBar.isRepeatStart) { actualLineType = BarLineStyle.HeavyLight; } else if (firstOfSystem) { actualLineType = BarLineStyle.Regular; } else { actualLineType = BarLineStyle.None; } } return actualLineType; } static actualBarLine(bar, isRight, firstOfSystem) { const masterBar = bar.masterBar; const requestedLineType = isRight ? bar.barLineRight : bar.barLineLeft; let actualLineType; if (requestedLineType === BarLineStyle.Automatic) { actualLineType = Bar.automaticToActualType(masterBar, isRight, firstOfSystem); } else { actualLineType = requestedLineType; } return actualLineType; } addVoice(voice) { voice.bar = this; voice.index = this.voices.length; this.voices.push(voice); } finish(settings, sharedDataBag = null) { this.isMultiVoice = false; this._isEmpty = true; this._isRestOnly = true; for (let i = 0, j = this.voices.length; i < j; i++) { const voice = this.voices[i]; voice.finish(settings, sharedDataBag); if (i > 0 && !voice.isEmpty) { this.isMultiVoice = true; } if (!voice.isEmpty) { this._isEmpty = false; } if (!voice.isRestOnly) { this._isRestOnly = false; } } // chain sustain pedal markers (and merge overlaps) const sustainPedals = this.sustainPedals; if (sustainPedals.length > 0) { let previousMarker = null; this.sustainPedals = []; if (this.previousBar && this.previousBar.sustainPedals.length > 0) { previousMarker = this.previousBar.sustainPedals[this.previousBar.sustainPedals.length - 1]; } const isDown = previousMarker !== null && previousMarker.pedalType !== SustainPedalMarkerType.Up; for (const marker of sustainPedals) { if (previousMarker && previousMarker.pedalType !== SustainPedalMarkerType.Up) { //duplicate or out-of-order markers if (previousMarker.bar === this && marker.ratioPosition <= previousMarker.ratioPosition) { continue; } previousMarker.nextPedalMarker = marker; marker.previousPedalMarker = previousMarker; } if (isDown && marker.pedalType === SustainPedalMarkerType.Down) { marker.pedalType = SustainPedalMarkerType.Hold; } marker.bar = this; this.sustainPedals.push(marker); previousMarker = marker; } } else if (this.previousBar && this.previousBar.sustainPedals.length > 0) { const lastMarker = this.previousBar.sustainPedals[this.previousBar.sustainPedals.length - 1]; if (lastMarker.pedalType !== SustainPedalMarkerType.Up) { // create hold marker if the last marker on the previous bar is not "up" const holdMarker = new SustainPedalMarker(); holdMarker.ratioPosition = 0; holdMarker.bar = this; holdMarker.pedalType = SustainPedalMarkerType.Hold; this.sustainPedals.push(holdMarker); lastMarker.nextPedalMarker = holdMarker; holdMarker.previousPedalMarker = lastMarker; } } } calculateDuration() { let duration = 0; for (const voice of this.voices) { const voiceDuration = voice.calculateDuration(); if (voiceDuration > duration) { duration = voiceDuration; } } return duration; } } Bar._globalBarId = 0; /** * Lists all dynamics. */ var DynamicValue; (function (DynamicValue) { // common dynamics /** * pianississimo (very very soft) */ DynamicValue[DynamicValue["PPP"] = 0] = "PPP"; /** * pianissimo (very soft) */ DynamicValue[DynamicValue["PP"] = 1] = "PP"; /** * piano (soft) */ DynamicValue[DynamicValue["P"] = 2] = "P"; /** * mezzo-piano (half soft) */ DynamicValue[DynamicValue["MP"] = 3] = "MP"; /** * mezzo-forte (half loud) */ DynamicValue[DynamicValue["MF"] = 4] = "MF"; /** * forte (loud) */ DynamicValue[DynamicValue["F"] = 5] = "F"; /** * fortissimo (very loud) */ DynamicValue[DynamicValue["FF"] = 6] = "FF"; /** * fortississimo (very very loud) */ DynamicValue[DynamicValue["FFF"] = 7] = "FFF"; // special dynamics DynamicValue[DynamicValue["PPPP"] = 8] = "PPPP"; DynamicValue[DynamicValue["PPPPP"] = 9] = "PPPPP"; DynamicValue[DynamicValue["PPPPPP"] = 10] = "PPPPPP"; DynamicValue[DynamicValue["FFFF"] = 11] = "FFFF"; DynamicValue[DynamicValue["FFFFF"] = 12] = "FFFFF"; DynamicValue[DynamicValue["FFFFFF"] = 13] = "FFFFFF"; /** * Sforzando */ DynamicValue[DynamicValue["SF"] = 14] = "SF"; /** * SforzandoPiano */ DynamicValue[DynamicValue["SFP"] = 15] = "SFP"; /** * SforzandoPianissimo */ DynamicValue[DynamicValue["SFPP"] = 16] = "SFPP"; /** * FortePiano */ DynamicValue[DynamicValue["FP"] = 17] = "FP"; /** * Rinforzando 1 */ DynamicValue[DynamicValue["RF"] = 18] = "RF"; /** * Rinforzando 2 */ DynamicValue[DynamicValue["RFZ"] = 19] = "RFZ"; /** * Sforzato */ DynamicValue[DynamicValue["SFZ"] = 20] = "SFZ"; /** * SforzatoFF */ DynamicValue[DynamicValue["SFFZ"] = 21] = "SFFZ"; /** * Forzando */ DynamicValue[DynamicValue["FZ"] = 22] = "FZ"; /** * Niente */ DynamicValue[DynamicValue["N"] = 23] = "N"; /** * Poco forte */ DynamicValue[DynamicValue["PF"] = 24] = "PF"; /** * SforzatoPiano */ DynamicValue[DynamicValue["SFZP"] = 25] = "SFZP"; })(DynamicValue || (DynamicValue = {})); class MidiUtils { /** * Converts the given midi tick duration into milliseconds. * @param ticks The duration in midi ticks * @param tempo The current tempo in BPM. * @returns The converted duration in milliseconds. */ static ticksToMillis(ticks, tempo) { return (ticks * (60000.0 / (tempo * MidiUtils.QuarterTime))) | 0; } /** * Converts the given midi tick duration into milliseconds. * @param millis The duration in milliseconds * @param tempo The current tempo in BPM. * @returns The converted duration in midi ticks. */ static millisToTicks(millis, tempo) { return (millis / (60000.0 / (tempo * MidiUtils.QuarterTime))) | 0; } /** * Converts a duration value to its ticks equivalent. */ static toTicks(duration) { return MidiUtils.valueToTicks(duration); } /** * Converts a numerical value to its ticks equivalent. * @param duration the numerical proportion to convert. (i.E. timesignature denominator, note duration,...) */ static valueToTicks(duration) { let denomninator = duration; if (denomninator < 0) { denomninator = 1 / -denomninator; } return (MidiUtils.QuarterTime * (4.0 / denomninator)) | 0; } static applyDot(ticks, doubleDotted) { if (doubleDotted) { return ticks + ((ticks / 4) | 0) * 3; } return ticks + ((ticks / 2) | 0); } static applyTuplet(ticks, numerator, denominator) { return ((ticks * denominator) / numerator) | 0; } static removeTuplet(ticks, numerator, denominator) { return ((ticks * numerator) / denominator) | 0; } static dynamicToVelocity(dynamicValue, adjustment = 0) { let velocity = 1; switch (dynamicValue) { case DynamicValue.PPP: velocity = MidiUtils.MinVelocity + 0 * MidiUtils.VelocityIncrement; break; case DynamicValue.PP: velocity = MidiUtils.MinVelocity + 1 * MidiUtils.VelocityIncrement; break; case DynamicValue.P: velocity = MidiUtils.MinVelocity + 2 * MidiUtils.VelocityIncrement; break; case DynamicValue.MP: velocity = MidiUtils.MinVelocity + 3 * MidiUtils.VelocityIncrement; break; case DynamicValue.MF: velocity = MidiUtils.MinVelocity + 4 * MidiUtils.VelocityIncrement; break; case DynamicValue.F: velocity = MidiUtils.MinVelocity + 5 * MidiUtils.VelocityIncrement; break; case DynamicValue.FF: velocity = MidiUtils.MinVelocity + 6 * MidiUtils.VelocityIncrement; break; case DynamicValue.FFF: velocity = MidiUtils.MinVelocity + 7 * MidiUtils.VelocityIncrement; break; // special case DynamicValue.PPPP: velocity = 10; break; case DynamicValue.PPPPP: velocity = 5; break; case DynamicValue.PPPPPP: velocity = 3; break; case DynamicValue.FFFF: velocity = MidiUtils.MinVelocity + 8 * MidiUtils.VelocityIncrement; break; case DynamicValue.FFFFF: velocity = MidiUtils.MinVelocity + 9 * MidiUtils.VelocityIncrement; break; case DynamicValue.FFFFFF: velocity = MidiUtils.MinVelocity + 10 * MidiUtils.VelocityIncrement; break; // "forced" variants -> a bit louder than normal, same as FF for us case DynamicValue.SF: case DynamicValue.SFP: case DynamicValue.SFZP: case DynamicValue.SFPP: case DynamicValue.SFZ: case DynamicValue.FZ: velocity = MidiUtils.MinVelocity + 6 * MidiUtils.VelocityIncrement; break; // force -> piano, same as F for us case DynamicValue.FP: velocity = MidiUtils.MinVelocity + 5 * MidiUtils.VelocityIncrement; break; // "rinforced" varaints -> like "forced" but typically for a whole passage // not a single note, same as FF for us case DynamicValue.RF: case DynamicValue.RFZ: case DynamicValue.SFFZ: velocity = MidiUtils.MinVelocity + 5 * MidiUtils.VelocityIncrement; break; // almost not hearable but still a value case DynamicValue.N: velocity = 1; break; // A bit weaker than standard F but stronger than MF case DynamicValue.PF: velocity = MidiUtils.MinVelocity + ((4.5 * MidiUtils.VelocityIncrement) | 0); break; } // 0 would means note-off (not played) so we need a minimum of 1 to have still a note played velocity += adjustment * MidiUtils.VelocityIncrement; return Math.min(Math.max(velocity, 1), 127); } } MidiUtils.QuarterTime = 960; MidiUtils.MinVelocity = 15; MidiUtils.VelocityIncrement = 16; /** * This public enumeration lists all types of automations. */ var AutomationType; (function (AutomationType) { /** * Tempo change. */ AutomationType[AutomationType["Tempo"] = 0] = "Tempo"; /** * Colume change. */ AutomationType[AutomationType["Volume"] = 1] = "Volume"; /** * Instrument change. */ AutomationType[AutomationType["Instrument"] = 2] = "Instrument"; /** * Balance change. */ AutomationType[AutomationType["Balance"] = 3] = "Balance"; /** * A sync point for synchronizing the internal time axis with an external audio track. */ AutomationType[AutomationType["SyncPoint"] = 4] = "SyncPoint"; })(AutomationType || (AutomationType = {})); /** * Represents the data of a sync point for synchronizing the internal time axis with * an external audio file. * @cloneable * @json * @json_strict */ class SyncPointData { constructor() { /** * Indicates for which repeat occurence this sync point is valid (e.g. 0 on the first time played, 1 on the second time played) */ this.barOccurence = 0; /** * The audio offset marking the position within the audio track in milliseconds. * This information is used to regularly sync (or on seeking) to match a given external audio time axis with the internal time axis. */ this.millisecondOffset = 0; } } /** * Automations are used to change the behaviour of a song. * @cloneable * @json * @json_strict */ class Automation { constructor() { /** * Gets or sets whether the automation is applied linear. */ this.isLinear = false; /** * Gets or sets the type of the automation. */ this.type = AutomationType.Tempo; /** * Gets or sets the target value of the automation. */ this.value = 0; /** * Gets or sets the relative position of of the automation. */ this.ratioPosition = 0; /** * Gets or sets the additional text of the automation. */ this.text = ''; } static buildTempoAutomation(isLinear, ratioPosition, value, reference) { if (reference < 1 || reference > 5) { reference = 2; } const references = new Float32Array([1, 0.5, 1.0, 1.5, 2.0, 3.0]); const automation = new Automation(); automation.type = AutomationType.Tempo; automation.isLinear = isLinear; automation.ratioPosition = ratioPosition; automation.value = value * references[reference]; return automation; } static buildInstrumentAutomation(isLinear, ratioPosition, value) { const automation = new Automation(); automation.type = AutomationType.Instrument; automation.isLinear = isLinear; automation.ratioPosition = ratioPosition; automation.value = value; return automation; } } /** * A single point of a bending graph. Used to * describe WhammyBar and String Bending effects. * @cloneable * @json * @json_strict */ class BendPoint { /** * Initializes a new instance of the {@link BendPoint} class. * @param offset The offset. * @param value The value. */ constructor(offset = 0, value = 0) { this.offset = offset; this.value = value; } } BendPoint.MaxPosition = 60; BendPoint.MaxValue = 12; /** * Lists the different bend styles */ var BendStyle; (function (BendStyle) { /** * The bends are as described by the bend points */ BendStyle[BendStyle["Default"] = 0] = "Default"; /** * The bends are gradual over the beat duration. */ BendStyle[BendStyle["Gradual"] = 1] = "Gradual"; /** * The bends are done fast before the next note. */ BendStyle[BendStyle["Fast"] = 2] = "Fast"; })(BendStyle || (BendStyle = {})); /** * Lists all types of bends */ var BendType; (function (BendType) { /** * No bend at all */ BendType[BendType["None"] = 0] = "None"; /** * Individual points define the bends in a flexible manner. * This system was mainly used in Guitar Pro 3-5 */ BendType[BendType["Custom"] = 1] = "Custom"; /** * Simple Bend from an unbended string to a higher note. */ BendType[BendType["Bend"] = 2] = "Bend"; /** * Release of a bend that was started on an earlier note. */ BendType[BendType["Release"] = 3] = "Release"; /** * A bend that starts from an unbended string, * and also releases the bend after some time. */ BendType[BendType["BendRelease"] = 4] = "BendRelease"; /** * Holds a bend that was started on an earlier note */ BendType[BendType["Hold"] = 5] = "Hold"; /** * A bend that is already started before the note is played then it is held until the end. */ BendType[BendType["Prebend"] = 6] = "Prebend"; /** * A bend that is already started before the note is played and * bends even further, then it is held until the end. */ BendType[BendType["PrebendBend"] = 7] = "PrebendBend"; /** * A bend that is already started before the note is playe